@poncho-ai/harness 0.23.0 → 0.24.0

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/harness@0.23.0 build /Users/cesar/Dev/latitude/poncho-ai/packages/harness
2
+ > @poncho-ai/harness@0.24.0 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
3
3
  > node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
4
4
 
5
5
  [embed-docs] Generated poncho-docs.ts with 4 topics
@@ -8,8 +8,8 @@
8
8
  CLI tsup v8.5.1
9
9
  CLI Target: es2022
10
10
  ESM Build start
11
- ESM dist/index.js 261.68 KB
12
- ESM ⚡️ Build success in 128ms
11
+ ESM dist/index.js 263.89 KB
12
+ ESM ⚡️ Build success in 136ms
13
13
  DTS Build start
14
- DTS ⚡️ Build success in 4463ms
15
- DTS dist/index.d.ts 27.40 KB
14
+ DTS ⚡️ Build success in 6918ms
15
+ DTS dist/index.d.ts 27.50 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.24.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`aee4f17`](https://github.com/cesr/poncho-ai/commit/aee4f17237d33b2cc134ed9934b709d967ca3f10) Thanks [@cesr](https://github.com/cesr)! - Add `edit_file` built-in tool with str_replace semantics for targeted file edits. The tool takes `path`, `old_str`, and `new_str` parameters, enforces uniqueness of the match, and is write-gated like `write_file` (disabled in production by default). Also improves browser SSE frame streaming with backpressure handling and auto-stops screencast when all listeners disconnect.
8
+
3
9
  ## 0.23.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -328,6 +328,7 @@ type BuiltInToolToggles = {
328
328
  list_directory?: boolean;
329
329
  read_file?: boolean;
330
330
  write_file?: boolean;
331
+ edit_file?: boolean;
331
332
  delete_file?: boolean;
332
333
  delete_directory?: boolean;
333
334
  };
@@ -434,6 +435,7 @@ declare const loadPonchoConfig: (workingDir: string) => Promise<PonchoConfig | u
434
435
 
435
436
  declare const createDefaultTools: (workingDir: string) => ToolDefinition[];
436
437
  declare const createWriteTool: (workingDir: string) => ToolDefinition;
438
+ declare const createEditTool: (workingDir: string) => ToolDefinition;
437
439
  declare const createDeleteTool: (workingDir: string) => ToolDefinition;
438
440
  declare const createDeleteDirectoryTool: (workingDir: string) => ToolDefinition;
439
441
  declare const ponchoDocsTool: ToolDefinition;
@@ -762,4 +764,4 @@ declare class TelemetryEmitter {
762
764
 
763
765
  declare const createSubagentTools: (manager: SubagentManager, getConversationId: () => string | undefined, getOwnerId: () => string) => ToolDefinition[];
764
766
 
765
- export { type AgentFrontmatter, AgentHarness, type AgentIdentity, type AgentLimitsConfig, type AgentModelConfig, type BuiltInToolToggles, type CompactMessagesOptions, type CompactResult, type CompactionConfig, type Conversation, type ConversationState, type ConversationStore, type ConversationSummary, type CronJobConfig, type HarnessOptions, type HarnessRunOutput, InMemoryConversationStore, InMemoryStateStore, LatitudeCapture, type LatitudeCaptureConfig, LocalMcpBridge, LocalUploadStore, type MainMemory, type McpConfig, type MemoryConfig, type MemoryStore, type MessagingChannelConfig, type ModelProviderFactory, PONCHO_UPLOAD_SCHEME, type ParsedAgent, type PonchoConfig, type ProviderConfig, type RemoteMcpServerConfig, type RuntimeRenderContext, S3UploadStore, STORAGE_SCHEMA_VERSION, type SkillContextEntry, type SkillMetadata, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type SubagentManager, type SubagentResult, type SubagentSummary, type TelemetryConfig, TelemetryEmitter, type ToolAccess, type ToolCall, ToolDispatcher, type ToolExecutionResult, type UploadStore, type UploadsConfig, VercelBlobUploadStore, buildAgentDirectoryName, buildSkillContextWindow, compactMessages, createConversationStore, createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createMemoryStore, createMemoryTools, createModelProvider, createSkillTools, createStateStore, createSubagentTools, createUploadStore, createWriteTool, deriveUploadKey, ensureAgentIdentity, estimateTokens, estimateTotalTokens, findSafeSplitPoint, generateAgentId, getAgentStoreDirectory, getModelContextWindow, getPonchoStoreRoot, jsonSchemaToZod, loadPonchoConfig, loadSkillContext, loadSkillInstructions, loadSkillMetadata, normalizeScriptPolicyPath, parseAgentFile, parseAgentMarkdown, ponchoDocsTool, readSkillResource, renderAgentPrompt, resolveAgentIdentity, resolveCompactionConfig, resolveMemoryConfig, resolveSkillDirs, resolveStateConfig, slugifyStorageComponent };
767
+ export { type AgentFrontmatter, AgentHarness, type AgentIdentity, type AgentLimitsConfig, type AgentModelConfig, type BuiltInToolToggles, type CompactMessagesOptions, type CompactResult, type CompactionConfig, type Conversation, type ConversationState, type ConversationStore, type ConversationSummary, type CronJobConfig, type HarnessOptions, type HarnessRunOutput, InMemoryConversationStore, InMemoryStateStore, LatitudeCapture, type LatitudeCaptureConfig, LocalMcpBridge, LocalUploadStore, type MainMemory, type McpConfig, type MemoryConfig, type MemoryStore, type MessagingChannelConfig, type ModelProviderFactory, PONCHO_UPLOAD_SCHEME, type ParsedAgent, type PonchoConfig, type ProviderConfig, type RemoteMcpServerConfig, type RuntimeRenderContext, S3UploadStore, STORAGE_SCHEMA_VERSION, type SkillContextEntry, type SkillMetadata, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type SubagentManager, type SubagentResult, type SubagentSummary, type TelemetryConfig, TelemetryEmitter, type ToolAccess, type ToolCall, ToolDispatcher, type ToolExecutionResult, type UploadStore, type UploadsConfig, VercelBlobUploadStore, buildAgentDirectoryName, buildSkillContextWindow, compactMessages, createConversationStore, createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createMemoryStore, createMemoryTools, createModelProvider, createSkillTools, createStateStore, createSubagentTools, createUploadStore, createWriteTool, deriveUploadKey, ensureAgentIdentity, estimateTokens, estimateTotalTokens, findSafeSplitPoint, generateAgentId, getAgentStoreDirectory, getModelContextWindow, getPonchoStoreRoot, jsonSchemaToZod, loadPonchoConfig, loadSkillContext, loadSkillInstructions, loadSkillMetadata, normalizeScriptPolicyPath, parseAgentFile, parseAgentMarkdown, ponchoDocsTool, readSkillResource, renderAgentPrompt, resolveAgentIdentity, resolveCompactionConfig, resolveMemoryConfig, resolveSkillDirs, resolveStateConfig, slugifyStorageComponent };
package/dist/index.js CHANGED
@@ -1908,6 +1908,52 @@ var createWriteTool = (workingDir) => defineTool({
1908
1908
  return { path, written: true };
1909
1909
  }
1910
1910
  });
1911
+ var createEditTool = (workingDir) => defineTool({
1912
+ name: "edit_file",
1913
+ description: "Edit a file by replacing an exact string match with new content. The old_str must match exactly one location in the file (including whitespace and indentation). Use an empty new_str to delete matched content.",
1914
+ inputSchema: {
1915
+ type: "object",
1916
+ properties: {
1917
+ path: {
1918
+ type: "string",
1919
+ description: "File path relative to working directory"
1920
+ },
1921
+ old_str: {
1922
+ type: "string",
1923
+ description: "The exact text to find and replace (must be unique in the file). Include surrounding context lines if needed to ensure uniqueness."
1924
+ },
1925
+ new_str: {
1926
+ type: "string",
1927
+ description: "The replacement text (use empty string to delete the matched content)"
1928
+ }
1929
+ },
1930
+ required: ["path", "old_str", "new_str"],
1931
+ additionalProperties: false
1932
+ },
1933
+ handler: async (input) => {
1934
+ const path = typeof input.path === "string" ? input.path : "";
1935
+ const oldStr = typeof input.old_str === "string" ? input.old_str : "";
1936
+ const newStr = typeof input.new_str === "string" ? input.new_str : "";
1937
+ if (!oldStr) throw new Error("old_str must not be empty.");
1938
+ const resolved = resolveSafePath(workingDir, path);
1939
+ const content = await readFile3(resolved, "utf8");
1940
+ const first = content.indexOf(oldStr);
1941
+ if (first === -1) {
1942
+ throw new Error(
1943
+ "old_str not found in file. Make sure it matches exactly, including whitespace and line breaks."
1944
+ );
1945
+ }
1946
+ const last = content.lastIndexOf(oldStr);
1947
+ if (first !== last) {
1948
+ throw new Error(
1949
+ "old_str appears multiple times in the file. Please provide more context to ensure a unique match."
1950
+ );
1951
+ }
1952
+ const newContent = content.slice(0, first) + newStr + content.slice(first + oldStr.length);
1953
+ await writeFile2(resolved, newContent, "utf8");
1954
+ return { path, edited: true };
1955
+ }
1956
+ });
1911
1957
  var createDeleteTool = (workingDir) => defineTool({
1912
1958
  name: "delete_file",
1913
1959
  description: "Delete a file at a path inside the working directory",
@@ -4327,7 +4373,7 @@ You are running locally in development mode. Treat this as an editable agent wor
4327
4373
  ## Understanding Your Environment
4328
4374
 
4329
4375
  - Built-in tools: \`list_directory\` and \`read_file\`
4330
- - \`write_file\` is available in development (disabled by default in production)
4376
+ - \`write_file\` and \`edit_file\` are available in development (disabled by default in production)
4331
4377
  - A starter local skill is included (\`starter-echo\`)
4332
4378
  - Bash/shell commands are **not** available unless you install and enable a shell tool/skill
4333
4379
  - Git operations are only available if a git-capable tool/skill is configured
@@ -4588,6 +4634,7 @@ Since all fields have defaults, you only need to specify \`*Env\` when your env
4588
4634
  - If shell/CLI access is unavailable, ask the user to run needed commands and provide exact copy-paste commands.
4589
4635
  - For setup, skills, MCP, auth, storage, telemetry, or "how do I..." questions, proactively read \`README.md\` with \`read_file\` before answering.
4590
4636
  - Prefer quoting concrete commands and examples from \`README.md\` over guessing.
4637
+ - Prefer \`edit_file\` for targeted changes to existing files (uses exact string matching); use \`write_file\` only for creating new files or full rewrites.
4591
4638
  - Keep edits minimal, preserve unrelated settings/code, and summarize what changed.
4592
4639
 
4593
4640
  ## Detailed Documentation
@@ -4658,7 +4705,7 @@ var AgentHarness = class _AgentHarness {
4658
4705
  isToolEnabled(name) {
4659
4706
  const access3 = this.resolveToolAccess(name);
4660
4707
  if (access3 === false) return false;
4661
- if (name === "write_file" || name === "delete_file" || name === "delete_directory") {
4708
+ if (name === "write_file" || name === "edit_file" || name === "delete_file" || name === "delete_directory") {
4662
4709
  return this.shouldEnableWriteTool();
4663
4710
  }
4664
4711
  return true;
@@ -4701,6 +4748,9 @@ var AgentHarness = class _AgentHarness {
4701
4748
  if (this.isToolEnabled("write_file")) {
4702
4749
  this.registerIfMissing(createWriteTool(this.workingDir));
4703
4750
  }
4751
+ if (this.isToolEnabled("edit_file")) {
4752
+ this.registerIfMissing(createEditTool(this.workingDir));
4753
+ }
4704
4754
  if (this.isToolEnabled("delete_file")) {
4705
4755
  this.registerIfMissing(createDeleteTool(this.workingDir));
4706
4756
  }
@@ -7412,6 +7462,7 @@ export {
7412
7462
  createDefaultTools,
7413
7463
  createDeleteDirectoryTool,
7414
7464
  createDeleteTool,
7465
+ createEditTool,
7415
7466
  createMemoryStore,
7416
7467
  createMemoryTools,
7417
7468
  createModelProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
package/src/config.ts CHANGED
@@ -39,6 +39,7 @@ export type BuiltInToolToggles = {
39
39
  list_directory?: boolean;
40
40
  read_file?: boolean;
41
41
  write_file?: boolean;
42
+ edit_file?: boolean;
42
43
  delete_file?: boolean;
43
44
  delete_directory?: boolean;
44
45
  };
@@ -89,6 +89,59 @@ export const createWriteTool = (workingDir: string): ToolDefinition =>
89
89
  },
90
90
  });
91
91
 
92
+ export const createEditTool = (workingDir: string): ToolDefinition =>
93
+ defineTool({
94
+ name: "edit_file",
95
+ description:
96
+ "Edit a file by replacing an exact string match with new content. " +
97
+ "The old_str must match exactly one location in the file (including whitespace and indentation). " +
98
+ "Use an empty new_str to delete matched content.",
99
+ inputSchema: {
100
+ type: "object",
101
+ properties: {
102
+ path: {
103
+ type: "string",
104
+ description: "File path relative to working directory",
105
+ },
106
+ old_str: {
107
+ type: "string",
108
+ description:
109
+ "The exact text to find and replace (must be unique in the file). " +
110
+ "Include surrounding context lines if needed to ensure uniqueness.",
111
+ },
112
+ new_str: {
113
+ type: "string",
114
+ description: "The replacement text (use empty string to delete the matched content)",
115
+ },
116
+ },
117
+ required: ["path", "old_str", "new_str"],
118
+ additionalProperties: false,
119
+ },
120
+ handler: async (input) => {
121
+ const path = typeof input.path === "string" ? input.path : "";
122
+ const oldStr = typeof input.old_str === "string" ? input.old_str : "";
123
+ const newStr = typeof input.new_str === "string" ? input.new_str : "";
124
+ if (!oldStr) throw new Error("old_str must not be empty.");
125
+ const resolved = resolveSafePath(workingDir, path);
126
+ const content = await readFile(resolved, "utf8");
127
+ const first = content.indexOf(oldStr);
128
+ if (first === -1) {
129
+ throw new Error(
130
+ "old_str not found in file. Make sure it matches exactly, including whitespace and line breaks.",
131
+ );
132
+ }
133
+ const last = content.lastIndexOf(oldStr);
134
+ if (first !== last) {
135
+ throw new Error(
136
+ "old_str appears multiple times in the file. Please provide more context to ensure a unique match.",
137
+ );
138
+ }
139
+ const newContent = content.slice(0, first) + newStr + content.slice(first + oldStr.length);
140
+ await writeFile(resolved, newContent, "utf8");
141
+ return { path, edited: true };
142
+ },
143
+ });
144
+
92
145
  export const createDeleteTool = (workingDir: string): ToolDefinition =>
93
146
  defineTool({
94
147
  name: "delete_file",
package/src/harness.ts CHANGED
@@ -15,7 +15,7 @@ import type { UploadStore } from "./upload-store.js";
15
15
  import { PONCHO_UPLOAD_SCHEME, deriveUploadKey } from "./upload-store.js";
16
16
  import { parseAgentFile, renderAgentPrompt, type ParsedAgent, type AgentFrontmatter } from "./agent-parser.js";
17
17
  import { loadPonchoConfig, resolveMemoryConfig, type PonchoConfig, type ToolAccess, type BuiltInToolToggles } from "./config.js";
18
- import { createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createWriteTool, ponchoDocsTool } from "./default-tools.js";
18
+ import { createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createWriteTool, ponchoDocsTool } from "./default-tools.js";
19
19
  import {
20
20
  createMemoryStore,
21
21
  createMemoryTools,
@@ -224,7 +224,7 @@ You are running locally in development mode. Treat this as an editable agent wor
224
224
  ## Understanding Your Environment
225
225
 
226
226
  - Built-in tools: \`list_directory\` and \`read_file\`
227
- - \`write_file\` is available in development (disabled by default in production)
227
+ - \`write_file\` and \`edit_file\` are available in development (disabled by default in production)
228
228
  - A starter local skill is included (\`starter-echo\`)
229
229
  - Bash/shell commands are **not** available unless you install and enable a shell tool/skill
230
230
  - Git operations are only available if a git-capable tool/skill is configured
@@ -485,6 +485,7 @@ Since all fields have defaults, you only need to specify \`*Env\` when your env
485
485
  - If shell/CLI access is unavailable, ask the user to run needed commands and provide exact copy-paste commands.
486
486
  - For setup, skills, MCP, auth, storage, telemetry, or "how do I..." questions, proactively read \`README.md\` with \`read_file\` before answering.
487
487
  - Prefer quoting concrete commands and examples from \`README.md\` over guessing.
488
+ - Prefer \`edit_file\` for targeted changes to existing files (uses exact string matching); use \`write_file\` only for creating new files or full rewrites.
488
489
  - Keep edits minimal, preserve unrelated settings/code, and summarize what changed.
489
490
 
490
491
  ## Detailed Documentation
@@ -585,7 +586,7 @@ export class AgentHarness {
585
586
  private isToolEnabled(name: string): boolean {
586
587
  const access = this.resolveToolAccess(name);
587
588
  if (access === false) return false;
588
- if (name === "write_file" || name === "delete_file" || name === "delete_directory") {
589
+ if (name === "write_file" || name === "edit_file" || name === "delete_file" || name === "delete_directory") {
589
590
  return this.shouldEnableWriteTool();
590
591
  }
591
592
  return true;
@@ -633,6 +634,9 @@ export class AgentHarness {
633
634
  if (this.isToolEnabled("write_file")) {
634
635
  this.registerIfMissing(createWriteTool(this.workingDir));
635
636
  }
637
+ if (this.isToolEnabled("edit_file")) {
638
+ this.registerIfMissing(createEditTool(this.workingDir));
639
+ }
636
640
  if (this.isToolEnabled("delete_file")) {
637
641
  this.registerIfMissing(createDeleteTool(this.workingDir));
638
642
  }
@@ -1,10 +1,11 @@
1
- import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
1
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { createServer } from "node:http";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "path";
5
5
  import { describe, expect, it } from "vitest";
6
6
  import type { ToolContext } from "@poncho-ai/sdk";
7
7
  import { AgentHarness } from "../src/harness.js";
8
+ import { createEditTool } from "../src/default-tools.js";
8
9
  import { loadSkillMetadata } from "../src/skill-context.js";
9
10
 
10
11
  const stubContext: ToolContext = {
@@ -39,6 +40,7 @@ model:
39
40
  expect(names).toContain("list_directory");
40
41
  expect(names).toContain("read_file");
41
42
  expect(names).toContain("write_file");
43
+ expect(names).toContain("edit_file");
42
44
  });
43
45
 
44
46
  it("disables write_file by default in production environment", async () => {
@@ -64,6 +66,7 @@ model:
64
66
  expect(names).toContain("list_directory");
65
67
  expect(names).toContain("read_file");
66
68
  expect(names).not.toContain("write_file");
69
+ expect(names).not.toContain("edit_file");
67
70
  });
68
71
 
69
72
  it("allows disabling built-in tools via poncho.config.js", async () => {
@@ -1230,3 +1233,63 @@ allowed-tools:
1230
1233
  });
1231
1234
 
1232
1235
  });
1236
+
1237
+ describe("edit_file tool", () => {
1238
+ it("replaces a unique string match in a file", async () => {
1239
+ const dir = await mkdtemp(join(tmpdir(), "poncho-edit-tool-"));
1240
+ const filePath = join(dir, "test.txt");
1241
+ await writeFile(filePath, "hello world\nfoo bar\nbaz qux\n", "utf8");
1242
+
1243
+ const tool = createEditTool(dir);
1244
+ const result = await tool.handler(
1245
+ { path: "test.txt", old_str: "foo bar", new_str: "replaced" },
1246
+ stubContext,
1247
+ );
1248
+
1249
+ expect(result).toEqual({ path: "test.txt", edited: true });
1250
+ const content = await readFile(filePath, "utf8");
1251
+ expect(content).toBe("hello world\nreplaced\nbaz qux\n");
1252
+ });
1253
+
1254
+ it("errors when old_str is not found in the file", async () => {
1255
+ const dir = await mkdtemp(join(tmpdir(), "poncho-edit-tool-notfound-"));
1256
+ await writeFile(join(dir, "test.txt"), "hello world\n", "utf8");
1257
+
1258
+ const tool = createEditTool(dir);
1259
+ await expect(
1260
+ tool.handler({ path: "test.txt", old_str: "nonexistent", new_str: "x" }, stubContext),
1261
+ ).rejects.toThrow("old_str not found in file");
1262
+ });
1263
+
1264
+ it("errors when old_str matches multiple locations", async () => {
1265
+ const dir = await mkdtemp(join(tmpdir(), "poncho-edit-tool-multi-"));
1266
+ await writeFile(join(dir, "test.txt"), "aaa\nbbb\naaa\n", "utf8");
1267
+
1268
+ const tool = createEditTool(dir);
1269
+ await expect(
1270
+ tool.handler({ path: "test.txt", old_str: "aaa", new_str: "ccc" }, stubContext),
1271
+ ).rejects.toThrow("old_str appears multiple times");
1272
+ });
1273
+
1274
+ it("deletes matched content when new_str is empty", async () => {
1275
+ const dir = await mkdtemp(join(tmpdir(), "poncho-edit-tool-delete-"));
1276
+ const filePath = join(dir, "test.txt");
1277
+ await writeFile(filePath, "keep this\nremove this\nkeep this too\n", "utf8");
1278
+
1279
+ const tool = createEditTool(dir);
1280
+ await tool.handler({ path: "test.txt", old_str: "remove this\n", new_str: "" }, stubContext);
1281
+
1282
+ const content = await readFile(filePath, "utf8");
1283
+ expect(content).toBe("keep this\nkeep this too\n");
1284
+ });
1285
+
1286
+ it("errors when old_str is empty", async () => {
1287
+ const dir = await mkdtemp(join(tmpdir(), "poncho-edit-tool-empty-"));
1288
+ await writeFile(join(dir, "test.txt"), "content\n", "utf8");
1289
+
1290
+ const tool = createEditTool(dir);
1291
+ await expect(
1292
+ tool.handler({ path: "test.txt", old_str: "", new_str: "x" }, stubContext),
1293
+ ).rejects.toThrow("old_str must not be empty");
1294
+ });
1295
+ });
@@ -1,6 +0,0 @@
1
-
2
- > @poncho-ai/harness@0.11.2 lint /Users/cesar/Dev/latitude/poncho-ai/packages/harness
3
- > eslint src/
4
-
5
- sh: eslint: command not found
6
-  ELIFECYCLE  Command failed.
@@ -1,135 +0,0 @@
1
-
2
- > @poncho-ai/harness@0.16.1 test /Users/cesar/Dev/latitude/poncho-ai/packages/harness
3
- > vitest
4
-
5
-
6
-  RUN  v1.6.1 /Users/cesar/Dev/latitude/poncho-ai/packages/harness
7
-
8
- ✓ test/telemetry.test.ts  (3 tests) 2ms
9
- [event] step:completed {"type":"step:completed","step":1,"duration":1}
10
- [event] step:started {"type":"step:started","step":2}
11
- ✓ test/schema-converter.test.ts  (27 tests) 19ms
12
- stdout | test/mcp.test.ts > mcp bridge protocol transports > discovers and calls tools over streamable HTTP
13
- [poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":1}
14
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":0}
15
-
16
- stdout | test/mcp.test.ts > mcp bridge protocol transports > selects discovered tools by requested patterns
17
- [poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":2}
18
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":1}
19
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":2,"filteredByPolicyCount":0,"filteredByIntentCount":0}
20
-
21
- ✓ test/agent-parser.test.ts  (10 tests) 24ms
22
- stdout | test/mcp.test.ts > mcp bridge protocol transports > skips discovery when bearer token env value is missing
23
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":0,"filteredByPolicyCount":0,"filteredByIntentCount":0}
24
-
25
- stderr | test/mcp.test.ts > mcp bridge protocol transports > skips discovery when bearer token env value is missing
26
- [poncho][mcp] {"event":"auth.token_missing","server":"remote","tokenEnv":"MISSING_TOKEN_ENV"}
27
-
28
- stdout | test/mcp.test.ts > mcp bridge protocol transports > returns actionable errors for 403 permission failures
29
- [poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":1}
30
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":0}
31
-
32
- ✓ test/mcp.test.ts  (6 tests) 81ms
33
- ✓ test/memory.test.ts  (4 tests) 56ms
34
- ✓ test/state.test.ts  (5 tests) 237ms
35
- ✓ test/model-factory.test.ts  (4 tests) 2ms
36
- ✓ test/agent-identity.test.ts  (2 tests) 43ms
37
- stdout | test/harness.test.ts > agent harness > registers default filesystem tools
38
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
39
-
40
- stdout | test/harness.test.ts > agent harness > disables write_file by default in production environment
41
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
42
-
43
- stdout | test/harness.test.ts > agent harness > allows disabling built-in tools via poncho.config.js
44
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
45
-
46
- stdout | test/harness.test.ts > agent harness > supports per-environment tool overrides
47
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
48
-
49
- stdout | test/harness.test.ts > agent harness > supports per-environment tool overrides
50
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
51
-
52
- stdout | test/harness.test.ts > agent harness > does not auto-register exported tool objects from skill scripts
53
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
54
-
55
- stdout | test/harness.test.ts > agent harness > refreshes skill metadata and tools in development mode
56
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
57
-
58
- stdout | test/harness.test.ts > agent harness > refreshes skill metadata and tools in development mode
59
- [poncho][mcp] {"event":"tools.cleared","reason":"skills:changed","requestedPatterns":[]}
60
- [poncho][mcp] {"event":"tools.cleared","reason":"activate:beta","requestedPatterns":[]}
61
-
62
- stdout | test/harness.test.ts > agent harness > prunes removed active skills after refresh in development mode
63
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
64
- [poncho][mcp] {"event":"tools.cleared","reason":"activate:obsolete","requestedPatterns":[]}
65
-
66
- stdout | test/harness.test.ts > agent harness > prunes removed active skills after refresh in development mode
67
- [poncho][mcp] {"event":"tools.cleared","reason":"skills:changed","requestedPatterns":[]}
68
-
69
- stdout | test/harness.test.ts > agent harness > does not refresh skills outside development mode
70
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
71
-
72
- stdout | test/harness.test.ts > agent harness > clears active skills when skill metadata changes in development mode
73
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
74
- [poncho][mcp] {"event":"tools.cleared","reason":"activate:alpha","requestedPatterns":[]}
75
-
76
- stdout | test/harness.test.ts > agent harness > clears active skills when skill metadata changes in development mode
77
- [poncho][mcp] {"event":"tools.cleared","reason":"skills:changed","requestedPatterns":[]}
78
-
79
- stdout | test/harness.test.ts > agent harness > lists skill scripts through list_skill_scripts
80
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
81
-
82
- stdout | test/harness.test.ts > agent harness > runs JavaScript/TypeScript skill scripts through run_skill_script
83
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
84
-
85
- stdout | test/harness.test.ts > agent harness > runs AGENT-scope scripts from root scripts directory
86
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
87
-
88
- stdout | test/harness.test.ts > agent harness > blocks path traversal in run_skill_script
89
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
90
-
91
- stdout | test/harness.test.ts > agent harness > requires allowed-tools entries for non-standard script directories
92
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
93
-
94
- stdout | test/harness.test.ts > agent harness > registers MCP tools dynamically for stacked active skills and supports deactivation
95
- [poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":2}
96
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
97
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":1}
98
- [poncho][mcp] {"event":"tools.refreshed","reason":"activate:skill-a","requestedPatterns":["remote/a"],"registeredCount":1,"activeSkills":["skill-a"]}
99
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":2,"registeredCount":2,"filteredByPolicyCount":0,"filteredByIntentCount":0}
100
- [poncho][mcp] {"event":"tools.refreshed","reason":"activate:skill-b","requestedPatterns":["remote/a","remote/b"],"registeredCount":2,"activeSkills":["skill-a","skill-b"]}
101
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":1}
102
- [poncho][mcp] {"event":"tools.refreshed","reason":"deactivate:skill-a","requestedPatterns":["remote/b"],"registeredCount":1,"activeSkills":["skill-b"]}
103
-
104
- stdout | test/harness.test.ts > agent harness > supports flat tool access config format
105
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
106
-
107
- stdout | test/harness.test.ts > agent harness > flat tool access takes priority over legacy defaults
108
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
109
-
110
- stdout | test/harness.test.ts > agent harness > byEnvironment overrides flat tool access
111
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
112
-
113
- stdout | test/harness.test.ts > agent harness > registerTools skips tools disabled via config
114
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
115
-
116
- stdout | test/harness.test.ts > agent harness > approval access level registers the tool but marks it for approval
117
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
118
-
119
- stdout | test/harness.test.ts > agent harness > tools without approval config do not require approval
120
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
121
-
122
- stdout | test/harness.test.ts > agent harness > allows in-flight MCP calls to finish after skill deactivation
123
- [poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":1}
124
- [poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
125
- [poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":0}
126
- [poncho][mcp] {"event":"tools.refreshed","reason":"activate:skill-slow","requestedPatterns":["remote/slow"],"registeredCount":1,"activeSkills":["skill-slow"]}
127
- [poncho][mcp] {"event":"tools.cleared","reason":"deactivate:skill-slow","requestedPatterns":[]}
128
-
129
- ✓ test/harness.test.ts  (25 tests) 291ms
130
-
131
-  Test Files  9 passed (9)
132
-  Tests  86 passed (86)
133
-  Start at  17:47:43
134
-  Duration  1.88s (transform 684ms, setup 1ms, collect 2.34s, tests 755ms, environment 2ms, prepare 1.27s)
135
-