@poncho-ai/harness 0.15.0 → 0.16.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.
- package/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-lint.log +6 -0
- package/.turbo/turbo-test.log +139 -0
- package/CHANGELOG.md +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +86 -2
- package/package.json +2 -2
- package/src/harness.ts +97 -2
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.
|
|
2
|
+
> @poncho-ai/harness@0.16.0 build /Users/cesar/Dev/latitude/poncho-ai/packages/harness
|
|
3
3
|
> tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m185.71 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 140ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
14
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m22.
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 7525ms
|
|
14
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m22.38 KB[39m
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
|
|
2
|
+
> @poncho-ai/harness@0.14.2 test /Users/cesar/Dev/latitude/poncho-ai/packages/harness
|
|
3
|
+
> vitest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[7m[1m[36m RUN [39m[22m[27m [36mv1.6.1[39m [90m/Users/cesar/Dev/latitude/poncho-ai/packages/harness[39m
|
|
7
|
+
|
|
8
|
+
[event] step:completed {"type":"step:completed","step":1,"duration":1}
|
|
9
|
+
[32m✓[39m test/telemetry.test.ts [2m ([22m[2m3 tests[22m[2m)[22m[90m 3[2mms[22m[39m
|
|
10
|
+
[event] step:started {"type":"step:started","step":2}
|
|
11
|
+
[32m✓[39m test/schema-converter.test.ts [2m ([22m[2m27 tests[22m[2m)[22m[90m 16[2mms[22m[39m
|
|
12
|
+
[90mstdout[2m | test/mcp.test.ts[2m > [22m[2mmcp bridge protocol transports[2m > [22m[2mdiscovers and calls tools over streamable HTTP[22m[39m
|
|
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
|
+
[32m✓[39m test/agent-parser.test.ts [2m ([22m[2m10 tests[22m[2m)[22m[90m 17[2mms[22m[39m
|
|
17
|
+
[32m✓[39m test/memory.test.ts [2m ([22m[2m4 tests[22m[2m)[22m[90m 16[2mms[22m[39m
|
|
18
|
+
[90mstdout[2m | test/mcp.test.ts[2m > [22m[2mmcp bridge protocol transports[2m > [22m[2mselects discovered tools by requested patterns[22m[39m
|
|
19
|
+
[poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":2}
|
|
20
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":1}
|
|
21
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":2,"filteredByPolicyCount":0,"filteredByIntentCount":0}
|
|
22
|
+
|
|
23
|
+
[90mstdout[2m | test/mcp.test.ts[2m > [22m[2mmcp bridge protocol transports[2m > [22m[2mskips discovery when bearer token env value is missing[22m[39m
|
|
24
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":0,"filteredByPolicyCount":0,"filteredByIntentCount":0}
|
|
25
|
+
|
|
26
|
+
[90mstderr[2m | test/mcp.test.ts[2m > [22m[2mmcp bridge protocol transports[2m > [22m[2mskips discovery when bearer token env value is missing[22m[39m
|
|
27
|
+
[poncho][mcp] {"event":"auth.token_missing","server":"remote","tokenEnv":"MISSING_TOKEN_ENV"}
|
|
28
|
+
|
|
29
|
+
[90mstdout[2m | test/mcp.test.ts[2m > [22m[2mmcp bridge protocol transports[2m > [22m[2mreturns actionable errors for 403 permission failures[22m[39m
|
|
30
|
+
[poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":1}
|
|
31
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":0}
|
|
32
|
+
|
|
33
|
+
[32m✓[39m test/mcp.test.ts [2m ([22m[2m6 tests[22m[2m)[22m[90m 97[2mms[22m[39m
|
|
34
|
+
[32m✓[39m test/state.test.ts [2m ([22m[2m5 tests[22m[2m)[22m[90m 217[2mms[22m[39m
|
|
35
|
+
[32m✓[39m test/agent-identity.test.ts [2m ([22m[2m2 tests[22m[2m)[22m[90m 15[2mms[22m[39m
|
|
36
|
+
[32m✓[39m test/model-factory.test.ts [2m ([22m[2m4 tests[22m[2m)[22m[90m 3[2mms[22m[39m
|
|
37
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mregisters default filesystem tools[22m[39m
|
|
38
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
39
|
+
|
|
40
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mdisables write_file by default in production environment[22m[39m
|
|
41
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
42
|
+
|
|
43
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mallows disabling built-in tools via poncho.config.js[22m[39m
|
|
44
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
45
|
+
|
|
46
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2msupports per-environment tool overrides[22m[39m
|
|
47
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
48
|
+
|
|
49
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2msupports per-environment tool overrides[22m[39m
|
|
50
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
51
|
+
|
|
52
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mdoes not auto-register exported tool objects from skill scripts[22m[39m
|
|
53
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
54
|
+
|
|
55
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mrefreshes skill metadata and tools in development mode[22m[39m
|
|
56
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
57
|
+
|
|
58
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mrefreshes skill metadata and tools in development mode[22m[39m
|
|
59
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"skills:changed","requestedPatterns":[]}
|
|
60
|
+
|
|
61
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mrefreshes skill metadata and tools in development mode[22m[39m
|
|
62
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"activate:beta","requestedPatterns":[]}
|
|
63
|
+
|
|
64
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mprunes removed active skills after refresh in development mode[22m[39m
|
|
65
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
66
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"activate:obsolete","requestedPatterns":[]}
|
|
67
|
+
|
|
68
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mprunes removed active skills after refresh in development mode[22m[39m
|
|
69
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"skills:changed","requestedPatterns":[]}
|
|
70
|
+
|
|
71
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mdoes not refresh skills outside development mode[22m[39m
|
|
72
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
73
|
+
|
|
74
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mclears active skills when skill metadata changes in development mode[22m[39m
|
|
75
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
76
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"activate:alpha","requestedPatterns":[]}
|
|
77
|
+
|
|
78
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mclears active skills when skill metadata changes in development mode[22m[39m
|
|
79
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"skills:changed","requestedPatterns":[]}
|
|
80
|
+
|
|
81
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mlists skill scripts through list_skill_scripts[22m[39m
|
|
82
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
83
|
+
|
|
84
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mruns JavaScript/TypeScript skill scripts through run_skill_script[22m[39m
|
|
85
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
86
|
+
|
|
87
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mruns AGENT-scope scripts from root scripts directory[22m[39m
|
|
88
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
89
|
+
|
|
90
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mblocks path traversal in run_skill_script[22m[39m
|
|
91
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
92
|
+
|
|
93
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mrequires allowed-tools entries for non-standard script directories[22m[39m
|
|
94
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
95
|
+
|
|
96
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mregisters MCP tools dynamically for stacked active skills and supports deactivation[22m[39m
|
|
97
|
+
[poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":2}
|
|
98
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
99
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":1}
|
|
100
|
+
[poncho][mcp] {"event":"tools.refreshed","reason":"activate:skill-a","requestedPatterns":["remote/a"],"registeredCount":1,"activeSkills":["skill-a"]}
|
|
101
|
+
|
|
102
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mregisters MCP tools dynamically for stacked active skills and supports deactivation[22m[39m
|
|
103
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":2,"registeredCount":2,"filteredByPolicyCount":0,"filteredByIntentCount":0}
|
|
104
|
+
[poncho][mcp] {"event":"tools.refreshed","reason":"activate:skill-b","requestedPatterns":["remote/a","remote/b"],"registeredCount":2,"activeSkills":["skill-a","skill-b"]}
|
|
105
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":1}
|
|
106
|
+
[poncho][mcp] {"event":"tools.refreshed","reason":"deactivate:skill-a","requestedPatterns":["remote/b"],"registeredCount":1,"activeSkills":["skill-b"]}
|
|
107
|
+
|
|
108
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2msupports flat tool access config format[22m[39m
|
|
109
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
110
|
+
|
|
111
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mflat tool access takes priority over legacy defaults[22m[39m
|
|
112
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
113
|
+
|
|
114
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mbyEnvironment overrides flat tool access[22m[39m
|
|
115
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
116
|
+
|
|
117
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mregisterTools skips tools disabled via config[22m[39m
|
|
118
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
119
|
+
|
|
120
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mapproval access level registers the tool but marks it for approval[22m[39m
|
|
121
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
122
|
+
|
|
123
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mtools without approval config do not require approval[22m[39m
|
|
124
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
125
|
+
|
|
126
|
+
[90mstdout[2m | test/harness.test.ts[2m > [22m[2magent harness[2m > [22m[2mallows in-flight MCP calls to finish after skill deactivation[22m[39m
|
|
127
|
+
[poncho][mcp] {"event":"catalog.loaded","server":"remote","discoveredCount":1}
|
|
128
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"initialize","requestedPatterns":[]}
|
|
129
|
+
[poncho][mcp] {"event":"tools.selected","requestedPatternCount":1,"registeredCount":1,"filteredByPolicyCount":0,"filteredByIntentCount":0}
|
|
130
|
+
[poncho][mcp] {"event":"tools.refreshed","reason":"activate:skill-slow","requestedPatterns":["remote/slow"],"registeredCount":1,"activeSkills":["skill-slow"]}
|
|
131
|
+
[poncho][mcp] {"event":"tools.cleared","reason":"deactivate:skill-slow","requestedPatterns":[]}
|
|
132
|
+
|
|
133
|
+
[32m✓[39m test/harness.test.ts [2m ([22m[2m25 tests[22m[2m)[22m[33m 365[2mms[22m[39m
|
|
134
|
+
|
|
135
|
+
[2m Test Files [22m [1m[32m9 passed[39m[22m[90m (9)[39m
|
|
136
|
+
[2m Tests [22m [1m[32m86 passed[39m[22m[90m (86)[39m
|
|
137
|
+
[2m Start at [22m 13:30:36
|
|
138
|
+
[2m Duration [22m 2.19s[2m (transform 1.11s, setup 0ms, collect 2.81s, tests 749ms, environment 5ms, prepare 1.23s)[22m
|
|
139
|
+
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @poncho-ai/harness
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`12f2845`](https://github.com/cesr/poncho-ai/commit/12f28457c20e650640ff2a1c1dbece2a6e4a9ddd) Thanks [@cesr](https://github.com/cesr)! - Add browser storage persistence (cookies/localStorage survive restarts via configured storage provider) and new `browser_content` tool for fast text extraction from pages.
|
|
8
|
+
|
|
9
|
+
## 0.15.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Fix browser session reconnection, tab lifecycle management, and web UI panel state handling.
|
|
14
|
+
|
|
15
|
+
- Updated dependencies []:
|
|
16
|
+
- @poncho-ai/sdk@1.1.1
|
|
17
|
+
|
|
3
18
|
## 0.15.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -486,6 +486,7 @@ declare class AgentHarness {
|
|
|
486
486
|
private registerSkillTools;
|
|
487
487
|
private refreshSkillsIfChanged;
|
|
488
488
|
initialize(): Promise<void>;
|
|
489
|
+
private buildBrowserStoragePersistence;
|
|
489
490
|
private initBrowserTools;
|
|
490
491
|
/** Conversation ID of the currently executing run (set during run, cleared after). */
|
|
491
492
|
private _currentRunConversationId?;
|
package/dist/index.js
CHANGED
|
@@ -3290,6 +3290,85 @@ var AgentHarness = class {
|
|
|
3290
3290
|
);
|
|
3291
3291
|
}
|
|
3292
3292
|
}
|
|
3293
|
+
async buildBrowserStoragePersistence(config, sessionId) {
|
|
3294
|
+
const provider = config.storage?.provider ?? config.state?.provider ?? "local";
|
|
3295
|
+
const stateKey = `poncho:browser:state:${sessionId}`;
|
|
3296
|
+
if (provider === "memory") return void 0;
|
|
3297
|
+
if (provider === "local") {
|
|
3298
|
+
const { resolve: pathResolve } = await import("path");
|
|
3299
|
+
const { homedir: home } = await import("os");
|
|
3300
|
+
const stateDir = pathResolve(home(), ".poncho", "browser-state");
|
|
3301
|
+
const filePath = pathResolve(stateDir, `${sessionId}.json`);
|
|
3302
|
+
return {
|
|
3303
|
+
async save(json) {
|
|
3304
|
+
const { mkdir: mkdir5, writeFile: writeFile6 } = await import("fs/promises");
|
|
3305
|
+
await mkdir5(stateDir, { recursive: true });
|
|
3306
|
+
await writeFile6(filePath, json, "utf8");
|
|
3307
|
+
},
|
|
3308
|
+
async load() {
|
|
3309
|
+
const { readFile: readFile8 } = await import("fs/promises");
|
|
3310
|
+
try {
|
|
3311
|
+
return await readFile8(filePath, "utf8");
|
|
3312
|
+
} catch {
|
|
3313
|
+
return void 0;
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
if (provider === "upstash") {
|
|
3319
|
+
const urlEnv = config.storage?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
|
|
3320
|
+
const tokenEnv = config.storage?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
|
|
3321
|
+
const baseUrl = (process.env[urlEnv] ?? "").replace(/\/+$/, "");
|
|
3322
|
+
const token = process.env[tokenEnv] ?? "";
|
|
3323
|
+
if (!baseUrl || !token) return void 0;
|
|
3324
|
+
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
|
|
3325
|
+
return {
|
|
3326
|
+
async save(json) {
|
|
3327
|
+
await fetch(`${baseUrl}/set/${encodeURIComponent(stateKey)}/${encodeURIComponent(json)}`, { method: "POST", headers });
|
|
3328
|
+
},
|
|
3329
|
+
async load() {
|
|
3330
|
+
const res = await fetch(`${baseUrl}/get/${encodeURIComponent(stateKey)}`, { headers });
|
|
3331
|
+
if (!res.ok) return void 0;
|
|
3332
|
+
const body = await res.json();
|
|
3333
|
+
return body.result ?? void 0;
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
if (provider === "redis") {
|
|
3338
|
+
const urlEnv = config.storage?.urlEnv ?? "REDIS_URL";
|
|
3339
|
+
const url = process.env[urlEnv] ?? "";
|
|
3340
|
+
if (!url) return void 0;
|
|
3341
|
+
let clientPromise;
|
|
3342
|
+
const getClient = () => {
|
|
3343
|
+
if (!clientPromise) {
|
|
3344
|
+
clientPromise = (async () => {
|
|
3345
|
+
try {
|
|
3346
|
+
const mod = await import("redis");
|
|
3347
|
+
const c = mod.createClient({ url });
|
|
3348
|
+
await c.connect();
|
|
3349
|
+
return c;
|
|
3350
|
+
} catch {
|
|
3351
|
+
return void 0;
|
|
3352
|
+
}
|
|
3353
|
+
})();
|
|
3354
|
+
}
|
|
3355
|
+
return clientPromise;
|
|
3356
|
+
};
|
|
3357
|
+
return {
|
|
3358
|
+
async save(json) {
|
|
3359
|
+
const c = await getClient();
|
|
3360
|
+
if (c) await c.set(stateKey, json);
|
|
3361
|
+
},
|
|
3362
|
+
async load() {
|
|
3363
|
+
const c = await getClient();
|
|
3364
|
+
if (!c) return void 0;
|
|
3365
|
+
const val = await c.get(stateKey);
|
|
3366
|
+
return val ?? void 0;
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
return void 0;
|
|
3371
|
+
}
|
|
3293
3372
|
async initBrowserTools(config) {
|
|
3294
3373
|
const spec = ["@poncho-ai", "browser"].join("/");
|
|
3295
3374
|
let browserMod;
|
|
@@ -3318,9 +3397,14 @@ var AgentHarness = class {
|
|
|
3318
3397
|
);
|
|
3319
3398
|
}
|
|
3320
3399
|
this._browserMod = browserMod;
|
|
3321
|
-
const browserCfg = typeof config.browser === "object" ? config.browser : {};
|
|
3400
|
+
const browserCfg = typeof config.browser === "object" ? { ...config.browser } : {};
|
|
3322
3401
|
const agentId = this.parsedAgent?.frontmatter.id ?? this.parsedAgent?.frontmatter.name ?? "default";
|
|
3323
|
-
const
|
|
3402
|
+
const sessionId = `poncho-${agentId}`;
|
|
3403
|
+
const storagePersistence = await this.buildBrowserStoragePersistence(config, sessionId);
|
|
3404
|
+
if (storagePersistence) {
|
|
3405
|
+
browserCfg.storagePersistence = storagePersistence;
|
|
3406
|
+
}
|
|
3407
|
+
const session = new browserMod.BrowserSession(sessionId, browserCfg);
|
|
3324
3408
|
this._browserSession = session;
|
|
3325
3409
|
const tools = browserMod.createBrowserTools(
|
|
3326
3410
|
() => session,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"redis": "^5.10.0",
|
|
32
32
|
"yaml": "^2.4.0",
|
|
33
33
|
"zod": "^3.22.0",
|
|
34
|
-
"@poncho-ai/sdk": "1.1.
|
|
34
|
+
"@poncho-ai/sdk": "1.1.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/mustache": "^4.2.6",
|
package/src/harness.ts
CHANGED
|
@@ -914,6 +914,94 @@ export class AgentHarness {
|
|
|
914
914
|
}
|
|
915
915
|
}
|
|
916
916
|
|
|
917
|
+
private async buildBrowserStoragePersistence(
|
|
918
|
+
config: PonchoConfig,
|
|
919
|
+
sessionId: string,
|
|
920
|
+
): Promise<{ save(json: string): Promise<void>; load(): Promise<string | undefined> } | undefined> {
|
|
921
|
+
const provider = config.storage?.provider ?? (config.state as Record<string, unknown> | undefined)?.provider as string | undefined ?? "local";
|
|
922
|
+
const stateKey = `poncho:browser:state:${sessionId}`;
|
|
923
|
+
|
|
924
|
+
if (provider === "memory") return undefined;
|
|
925
|
+
|
|
926
|
+
if (provider === "local") {
|
|
927
|
+
const { resolve: pathResolve } = await import("node:path");
|
|
928
|
+
const { homedir: home } = await import("node:os");
|
|
929
|
+
const stateDir = pathResolve(home(), ".poncho", "browser-state");
|
|
930
|
+
const filePath = pathResolve(stateDir, `${sessionId}.json`);
|
|
931
|
+
return {
|
|
932
|
+
async save(json: string) {
|
|
933
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
934
|
+
await mkdir(stateDir, { recursive: true });
|
|
935
|
+
await writeFile(filePath, json, "utf8");
|
|
936
|
+
},
|
|
937
|
+
async load() {
|
|
938
|
+
const { readFile } = await import("node:fs/promises");
|
|
939
|
+
try { return await readFile(filePath, "utf8"); } catch { return undefined; }
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (provider === "upstash") {
|
|
945
|
+
const urlEnv = config.storage?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
|
|
946
|
+
const tokenEnv = config.storage?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
|
|
947
|
+
const baseUrl = (process.env[urlEnv] ?? "").replace(/\/+$/, "");
|
|
948
|
+
const token = process.env[tokenEnv] ?? "";
|
|
949
|
+
if (!baseUrl || !token) return undefined;
|
|
950
|
+
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
|
|
951
|
+
return {
|
|
952
|
+
async save(json: string) {
|
|
953
|
+
await fetch(`${baseUrl}/set/${encodeURIComponent(stateKey)}/${encodeURIComponent(json)}`, { method: "POST", headers });
|
|
954
|
+
},
|
|
955
|
+
async load() {
|
|
956
|
+
const res = await fetch(`${baseUrl}/get/${encodeURIComponent(stateKey)}`, { headers });
|
|
957
|
+
if (!res.ok) return undefined;
|
|
958
|
+
const body = await res.json() as { result?: string | null };
|
|
959
|
+
return body.result ?? undefined;
|
|
960
|
+
},
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (provider === "redis") {
|
|
965
|
+
const urlEnv = config.storage?.urlEnv ?? "REDIS_URL";
|
|
966
|
+
const url = process.env[urlEnv] ?? "";
|
|
967
|
+
if (!url) return undefined;
|
|
968
|
+
let clientPromise: Promise<{ get(k: string): Promise<string | null>; set(k: string, v: string): Promise<unknown> } | undefined> | undefined;
|
|
969
|
+
const getClient = () => {
|
|
970
|
+
if (!clientPromise) {
|
|
971
|
+
clientPromise = (async () => {
|
|
972
|
+
try {
|
|
973
|
+
const mod = (await import("redis")) as unknown as {
|
|
974
|
+
createClient: (opts: { url: string }) => {
|
|
975
|
+
connect(): Promise<unknown>;
|
|
976
|
+
get(k: string): Promise<string | null>;
|
|
977
|
+
set(k: string, v: string): Promise<unknown>;
|
|
978
|
+
};
|
|
979
|
+
};
|
|
980
|
+
const c = mod.createClient({ url });
|
|
981
|
+
await c.connect();
|
|
982
|
+
return c;
|
|
983
|
+
} catch { return undefined; }
|
|
984
|
+
})();
|
|
985
|
+
}
|
|
986
|
+
return clientPromise;
|
|
987
|
+
};
|
|
988
|
+
return {
|
|
989
|
+
async save(json: string) {
|
|
990
|
+
const c = await getClient();
|
|
991
|
+
if (c) await c.set(stateKey, json);
|
|
992
|
+
},
|
|
993
|
+
async load() {
|
|
994
|
+
const c = await getClient();
|
|
995
|
+
if (!c) return undefined;
|
|
996
|
+
const val = await c.get(stateKey);
|
|
997
|
+
return val ?? undefined;
|
|
998
|
+
},
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
return undefined;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
917
1005
|
private async initBrowserTools(config: PonchoConfig): Promise<void> {
|
|
918
1006
|
const spec = ["@poncho-ai", "browser"].join("/");
|
|
919
1007
|
let browserMod: {
|
|
@@ -947,9 +1035,16 @@ export class AgentHarness {
|
|
|
947
1035
|
}
|
|
948
1036
|
|
|
949
1037
|
this._browserMod = browserMod;
|
|
950
|
-
const browserCfg = typeof config.browser === "object" ? config.browser : {};
|
|
1038
|
+
const browserCfg: Record<string, unknown> = typeof config.browser === "object" ? { ...config.browser } : {};
|
|
951
1039
|
const agentId = this.parsedAgent?.frontmatter.id ?? this.parsedAgent?.frontmatter.name ?? "default";
|
|
952
|
-
const
|
|
1040
|
+
const sessionId = `poncho-${agentId}`;
|
|
1041
|
+
|
|
1042
|
+
const storagePersistence = await this.buildBrowserStoragePersistence(config, sessionId);
|
|
1043
|
+
if (storagePersistence) {
|
|
1044
|
+
browserCfg.storagePersistence = storagePersistence;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const session = new browserMod.BrowserSession(sessionId, browserCfg);
|
|
953
1048
|
this._browserSession = session;
|
|
954
1049
|
|
|
955
1050
|
const tools = browserMod.createBrowserTools(
|