@abdwhb-png/pi-test-harness 0.7.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/CHANGELOG.md +161 -0
- package/LICENSE +21 -0
- package/README.md +673 -0
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +61 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/events.d.ts +6 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +33 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/mock-pi-script.mjs +176 -0
- package/dist/mock-pi.d.ts +32 -0
- package/dist/mock-pi.d.ts.map +1 -0
- package/dist/mock-pi.js +150 -0
- package/dist/mock-pi.js.map +1 -0
- package/dist/mock-tools.d.ts +51 -0
- package/dist/mock-tools.d.ts.map +1 -0
- package/dist/mock-tools.js +192 -0
- package/dist/mock-tools.js.map +1 -0
- package/dist/mock-ui.d.ts +13 -0
- package/dist/mock-ui.d.ts.map +1 -0
- package/dist/mock-ui.js +159 -0
- package/dist/mock-ui.js.map +1 -0
- package/dist/pi-loader-parity.d.ts +36 -0
- package/dist/pi-loader-parity.d.ts.map +1 -0
- package/dist/pi-loader-parity.js +60 -0
- package/dist/pi-loader-parity.js.map +1 -0
- package/dist/playbook.d.ts +44 -0
- package/dist/playbook.d.ts.map +1 -0
- package/dist/playbook.js +143 -0
- package/dist/playbook.js.map +1 -0
- package/dist/sandbox.d.ts +27 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +269 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/session.d.ts +13 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +187 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +32 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +46 -0
- package/dist/utils.js.map +1 -0
- package/package.json +84 -0
- package/skills/pi-test-harness/SKILL.md +451 -0
- package/skills/pi-test-harness/evals/evals.json +26 -0
- package/skills/pi-test-harness/references/api-reference.md +480 -0
- package/skills/pi-test-harness/references/mock-pi-cli.md +135 -0
- package/skills/pi-test-harness/references/mock-tools.md +176 -0
- package/skills/pi-test-harness/references/mock-ui.md +170 -0
- package/skills/pi-test-harness/references/playbook-dsl.md +209 -0
- package/skills/pi-test-harness/references/sandbox-install.md +113 -0
- package/src/diagnostics.ts +90 -0
- package/src/events.ts +43 -0
- package/src/index.ts +42 -0
- package/src/mock-pi-script.mjs +176 -0
- package/src/mock-pi.ts +169 -0
- package/src/mock-tools.ts +252 -0
- package/src/mock-ui.ts +196 -0
- package/src/pi-loader-parity.ts +61 -0
- package/src/playbook.ts +189 -0
- package/src/sandbox.ts +334 -0
- package/src/session.ts +249 -0
- package/src/types.ts +203 -0
- package/src/utils.ts +46 -0
package/dist/playbook.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlaybookStreamFn — replaces the model with scripted responses.
|
|
3
|
+
*
|
|
4
|
+
* The playbook is a queue of actions. Each streamFn call dequeues the next
|
|
5
|
+
* action and returns it as an AssistantMessageEventStream.
|
|
6
|
+
*/
|
|
7
|
+
import { createAssistantMessageEventStream, } from "@earendil-works/pi-ai";
|
|
8
|
+
import { formatPlaybookDiagnostic } from "./diagnostics.js";
|
|
9
|
+
// ── DSL builders ────────────────────────────────────────────
|
|
10
|
+
/** Chainable call action builder */
|
|
11
|
+
class CallAction {
|
|
12
|
+
action;
|
|
13
|
+
constructor(toolName, params) {
|
|
14
|
+
this.action = { type: "call", toolName, params };
|
|
15
|
+
}
|
|
16
|
+
then(callback) {
|
|
17
|
+
this.action.thenCallback = callback;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The model calls a tool.
|
|
23
|
+
* @param toolName Tool to call
|
|
24
|
+
* @param params Static params or function for late binding
|
|
25
|
+
*/
|
|
26
|
+
export function calls(toolName, params = {}) {
|
|
27
|
+
return new CallAction(toolName, params);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The model emits text. Agent loop ends for this turn.
|
|
31
|
+
*/
|
|
32
|
+
export function says(text) {
|
|
33
|
+
return { type: "say", text };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Define one user→model turn.
|
|
37
|
+
* @param prompt The actual user prompt text
|
|
38
|
+
* @param actions What the model does in response (call/say sequence)
|
|
39
|
+
*/
|
|
40
|
+
export function when(prompt, actions) {
|
|
41
|
+
return {
|
|
42
|
+
prompt,
|
|
43
|
+
actions: actions.map((a) => (a instanceof CallAction ? a.action : a)),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ── PlaybookStreamFn ────────────────────────────────────────
|
|
47
|
+
function resolveParams(params) {
|
|
48
|
+
if (!params)
|
|
49
|
+
return {};
|
|
50
|
+
if (typeof params === "function")
|
|
51
|
+
return params();
|
|
52
|
+
return params;
|
|
53
|
+
}
|
|
54
|
+
function createAssistantMessage(action, toolCallCounter) {
|
|
55
|
+
const content = [];
|
|
56
|
+
if (action.type === "say") {
|
|
57
|
+
content.push({ type: "text", text: action.text ?? "" });
|
|
58
|
+
}
|
|
59
|
+
else if (action.type === "call") {
|
|
60
|
+
const resolvedParams = resolveParams(action.params);
|
|
61
|
+
content.push({
|
|
62
|
+
type: "toolCall",
|
|
63
|
+
id: `playbook-tc-${toolCallCounter}`,
|
|
64
|
+
name: action.toolName,
|
|
65
|
+
arguments: resolvedParams,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
role: "assistant",
|
|
70
|
+
content,
|
|
71
|
+
api: "openai-responses",
|
|
72
|
+
provider: "test",
|
|
73
|
+
model: "playbook",
|
|
74
|
+
usage: {
|
|
75
|
+
input: 0,
|
|
76
|
+
output: 0,
|
|
77
|
+
cacheRead: 0,
|
|
78
|
+
cacheWrite: 0,
|
|
79
|
+
totalTokens: 0,
|
|
80
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
81
|
+
},
|
|
82
|
+
stopReason: action.type === "call" ? "toolUse" : "stop",
|
|
83
|
+
timestamp: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export function createPlaybookStreamFn(turns) {
|
|
87
|
+
// Flatten all turns into a single action queue
|
|
88
|
+
const queue = [];
|
|
89
|
+
for (const turn of turns) {
|
|
90
|
+
queue.push(...turn.actions);
|
|
91
|
+
}
|
|
92
|
+
const state = {
|
|
93
|
+
consumed: 0,
|
|
94
|
+
remaining: queue.length,
|
|
95
|
+
consumedActions: [],
|
|
96
|
+
pendingCallbacks: new Map(),
|
|
97
|
+
};
|
|
98
|
+
let toolCallCounter = 0;
|
|
99
|
+
const streamFn = (_model, _context, _options) => {
|
|
100
|
+
const stream = createAssistantMessageEventStream();
|
|
101
|
+
const action = queue.shift();
|
|
102
|
+
if (!action) {
|
|
103
|
+
// Playbook exhausted
|
|
104
|
+
const diagnostic = formatPlaybookDiagnostic("exhausted", state);
|
|
105
|
+
const fallback = {
|
|
106
|
+
role: "assistant",
|
|
107
|
+
content: [{ type: "text", text: `[PLAYBOOK EXHAUSTED] ${diagnostic}` }],
|
|
108
|
+
api: "openai-responses",
|
|
109
|
+
provider: "test",
|
|
110
|
+
model: "playbook",
|
|
111
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
|
|
112
|
+
stopReason: "stop",
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
};
|
|
115
|
+
queueMicrotask(() => {
|
|
116
|
+
stream.push({ type: "done", reason: "stop", message: fallback });
|
|
117
|
+
});
|
|
118
|
+
return stream;
|
|
119
|
+
}
|
|
120
|
+
state.consumed++;
|
|
121
|
+
state.remaining = queue.length;
|
|
122
|
+
state.consumedActions.push(action);
|
|
123
|
+
if (action.type === "call")
|
|
124
|
+
toolCallCounter++;
|
|
125
|
+
const message = createAssistantMessage(action, toolCallCounter);
|
|
126
|
+
// Register callback if present (keyed by tool call ID for uniqueness)
|
|
127
|
+
if (action.type === "call" && action.thenCallback) {
|
|
128
|
+
const tcContent = message.content.find((c) => c.type === "toolCall");
|
|
129
|
+
const tcId = tcContent && "id" in tcContent ? tcContent.id : action.toolName;
|
|
130
|
+
state.pendingCallbacks.set(tcId, action.thenCallback);
|
|
131
|
+
}
|
|
132
|
+
queueMicrotask(() => {
|
|
133
|
+
stream.push({
|
|
134
|
+
type: "done",
|
|
135
|
+
reason: message.stopReason === "toolUse" ? "toolUse" : "stop",
|
|
136
|
+
message,
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
return stream;
|
|
140
|
+
};
|
|
141
|
+
return { streamFn, state };
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=playbook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playbook.js","sourceRoot":"","sources":["../src/playbook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox install verification — verifies npm packages work when installed clean.
|
|
3
|
+
*
|
|
4
|
+
* 1. npm pack → tarball
|
|
5
|
+
* 2. Install in temp dir
|
|
6
|
+
* 3. DefaultResourceLoader discovers extensions/skills
|
|
7
|
+
* 4. Verify resources load without errors
|
|
8
|
+
* 5. Optional smoke test
|
|
9
|
+
*/
|
|
10
|
+
import type { SandboxOptions, SandboxResult } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Resolve `command` to something execFileSync can actually start.
|
|
13
|
+
*
|
|
14
|
+
* **Why this exists**: on Windows a bare `sfw` (or `npm`) is the `.cmd` shim, and
|
|
15
|
+
* child_process applies no PATHEXT when creating the process, so spawning it
|
|
16
|
+
* fails with ENOENT — which broke the documented `npmCommand: ["sfw", "npm"]`
|
|
17
|
+
* preset on the Windows integration matrix. Candidate extensions come from
|
|
18
|
+
* PATHEXT (or the usual Windows default) and are matched against PATH in the
|
|
19
|
+
* same order the platform itself would. An unmatched command is returned
|
|
20
|
+
* unchanged, so a genuinely missing executable still raises a clear ENOENT.
|
|
21
|
+
*
|
|
22
|
+
* Exported for unit testing — not part of the public API contract.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export declare function _resolveExecutable(command: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
|
|
26
|
+
export declare function verifySandboxInstall(options: SandboxOptions): Promise<SandboxResult>;
|
|
27
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAShE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,MAAM,CA0BR;AAwED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,aAAa,CAAC,CA2LxB"}
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox install verification — verifies npm packages work when installed clean.
|
|
3
|
+
*
|
|
4
|
+
* 1. npm pack → tarball
|
|
5
|
+
* 2. Install in temp dir
|
|
6
|
+
* 3. DefaultResourceLoader discovers extensions/skills
|
|
7
|
+
* 4. Verify resources load without errors
|
|
8
|
+
* 5. Optional smoke test
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import * as os from "node:os";
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { DefaultResourceLoader, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { withoutJitiNativeImport } from "./pi-loader-parity.js";
|
|
16
|
+
import { createTestSession } from "./session.js";
|
|
17
|
+
/** Resolve the npm command array. Defaults to platform-aware npm. */
|
|
18
|
+
function resolveNpmCommand(npmCommand) {
|
|
19
|
+
if (npmCommand && npmCommand.length > 0)
|
|
20
|
+
return npmCommand;
|
|
21
|
+
return [process.platform === "win32" ? "npm.cmd" : "npm"];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve `command` to something execFileSync can actually start.
|
|
25
|
+
*
|
|
26
|
+
* **Why this exists**: on Windows a bare `sfw` (or `npm`) is the `.cmd` shim, and
|
|
27
|
+
* child_process applies no PATHEXT when creating the process, so spawning it
|
|
28
|
+
* fails with ENOENT — which broke the documented `npmCommand: ["sfw", "npm"]`
|
|
29
|
+
* preset on the Windows integration matrix. Candidate extensions come from
|
|
30
|
+
* PATHEXT (or the usual Windows default) and are matched against PATH in the
|
|
31
|
+
* same order the platform itself would. An unmatched command is returned
|
|
32
|
+
* unchanged, so a genuinely missing executable still raises a clear ENOENT.
|
|
33
|
+
*
|
|
34
|
+
* Exported for unit testing — not part of the public API contract.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export function _resolveExecutable(command, platform = process.platform, env = process.env) {
|
|
38
|
+
if (platform !== "win32")
|
|
39
|
+
return command;
|
|
40
|
+
if (path.extname(command) !== "")
|
|
41
|
+
return command;
|
|
42
|
+
const extensions = (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD")
|
|
43
|
+
.split(";")
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
// An explicit path never goes through PATH resolution — only PATHEXT applies.
|
|
46
|
+
if (command.includes("/") || command.includes("\\")) {
|
|
47
|
+
for (const extension of extensions) {
|
|
48
|
+
const candidate = `${command}${extension}`;
|
|
49
|
+
if (fs.existsSync(candidate))
|
|
50
|
+
return candidate;
|
|
51
|
+
}
|
|
52
|
+
return command;
|
|
53
|
+
}
|
|
54
|
+
const directories = (env.PATH ?? "").split(path.delimiter).filter(Boolean);
|
|
55
|
+
for (const directory of directories) {
|
|
56
|
+
for (const extension of extensions) {
|
|
57
|
+
const candidate = path.join(directory, `${command}${extension}`);
|
|
58
|
+
if (fs.existsSync(candidate))
|
|
59
|
+
return candidate;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return command;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Run a command via execFileSync with safe string conversion for the full
|
|
66
|
+
* command line in the error message.
|
|
67
|
+
*/
|
|
68
|
+
function run(args, cwd, label) {
|
|
69
|
+
const [cmd, ...cmdArgs] = args;
|
|
70
|
+
try {
|
|
71
|
+
return execFileSync(_resolveExecutable(cmd), cmdArgs, {
|
|
72
|
+
cwd,
|
|
73
|
+
encoding: "utf-8",
|
|
74
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
75
|
+
}).trim();
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
// Enhance the error message with context
|
|
79
|
+
const stderr = err.stderr?.toString().trim() ?? "";
|
|
80
|
+
const enhanced = new Error(`${label} failed: ${err.message}${stderr ? `\nstderr: ${stderr}` : ""}`);
|
|
81
|
+
// Preserve the original error code (ENOENT, etc.)
|
|
82
|
+
enhanced.code = err.code;
|
|
83
|
+
throw enhanced;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Read a package.json, reporting the offending path instead of surfacing a bare
|
|
88
|
+
* SyntaxError. A malformed manifest is fatal for both call sites below, so the
|
|
89
|
+
* failure is rethrown with the path that caused it.
|
|
90
|
+
*/
|
|
91
|
+
function readPackageJson(filePath) {
|
|
92
|
+
let raw;
|
|
93
|
+
try {
|
|
94
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
throw new Error(`Could not read ${filePath}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(raw);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw new Error(`Invalid JSON in ${filePath}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Read the package name from a package.json, decoded here rather than at the
|
|
108
|
+
* call site: every consumer of this value needs it to be a non-empty string.
|
|
109
|
+
*/
|
|
110
|
+
function readPackageName(filePath) {
|
|
111
|
+
const manifest = readPackageJson(filePath);
|
|
112
|
+
const name = manifest.name;
|
|
113
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
114
|
+
throw new Error(`package.json at ${filePath} has no "name" field`);
|
|
115
|
+
}
|
|
116
|
+
return name;
|
|
117
|
+
}
|
|
118
|
+
export async function verifySandboxInstall(options) {
|
|
119
|
+
const packageDir = path.resolve(options.packageDir);
|
|
120
|
+
const npmCmd = resolveNpmCommand(options.npmCommand);
|
|
121
|
+
// Validate package directory
|
|
122
|
+
const pkgJsonPath = path.join(packageDir, "package.json");
|
|
123
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
124
|
+
throw new Error(`No package.json found at ${pkgJsonPath}`);
|
|
125
|
+
}
|
|
126
|
+
const pkgName = readPackageName(pkgJsonPath);
|
|
127
|
+
// Create sandbox temp dir
|
|
128
|
+
const sandboxDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-sandbox-"));
|
|
129
|
+
try {
|
|
130
|
+
// 1. npm pack → tarball
|
|
131
|
+
const packArgs = [...npmCmd.slice(1), "pack", "--pack-destination", "."];
|
|
132
|
+
const packOutput = run([...npmCmd.slice(0, 1), ...packArgs], packageDir, `npm pack in ${packageDir}`);
|
|
133
|
+
// The output is the tarball filename
|
|
134
|
+
const tarballName = packOutput.split("\n").pop().trim();
|
|
135
|
+
const tarballSrc = path.join(packageDir, tarballName);
|
|
136
|
+
const tarballDest = path.join(sandboxDir, tarballName);
|
|
137
|
+
try {
|
|
138
|
+
fs.copyFileSync(tarballSrc, tarballDest);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
// Always clean up tarball from source (even if copy fails)
|
|
142
|
+
try {
|
|
143
|
+
if (fs.existsSync(tarballSrc))
|
|
144
|
+
fs.unlinkSync(tarballSrc);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/* best-effort */
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// 2. Create minimal package.json in sandbox
|
|
151
|
+
const sandboxPkg = {
|
|
152
|
+
name: "pi-test-sandbox",
|
|
153
|
+
private: true,
|
|
154
|
+
type: "module",
|
|
155
|
+
dependencies: {
|
|
156
|
+
[pkgName]: `file:./${tarballName}`,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
fs.writeFileSync(path.join(sandboxDir, "package.json"), JSON.stringify(sandboxPkg, null, 2));
|
|
160
|
+
// 3. npm install
|
|
161
|
+
const installArgs = [...npmCmd.slice(1), "install", "--ignore-scripts=false"];
|
|
162
|
+
run([...npmCmd.slice(0, 1), ...installArgs], sandboxDir, `npm install in ${sandboxDir}`);
|
|
163
|
+
// 4. Find the installed package and use DefaultResourceLoader
|
|
164
|
+
const installedPkgDir = path.join(sandboxDir, "node_modules", ...pkgName.split("/"));
|
|
165
|
+
if (!fs.existsSync(installedPkgDir)) {
|
|
166
|
+
throw new Error(`Package not found after install: ${installedPkgDir}`);
|
|
167
|
+
}
|
|
168
|
+
// Read installed package.json for pi manifest
|
|
169
|
+
const installedPkgJson = readPackageJson(path.join(installedPkgDir, "package.json"));
|
|
170
|
+
const piManifest = installedPkgJson.pi;
|
|
171
|
+
// Resolve extension paths from the installed package
|
|
172
|
+
const extensionPaths = [];
|
|
173
|
+
if (piManifest?.extensions) {
|
|
174
|
+
for (const ext of piManifest.extensions) {
|
|
175
|
+
const resolved = path.resolve(installedPkgDir, ext);
|
|
176
|
+
if (fs.existsSync(resolved)) {
|
|
177
|
+
extensionPaths.push(resolved);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Try as glob/directory
|
|
181
|
+
const dir = path.resolve(installedPkgDir, ext);
|
|
182
|
+
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
|
|
183
|
+
const files = fs
|
|
184
|
+
.readdirSync(dir)
|
|
185
|
+
.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
|
|
186
|
+
extensionPaths.push(...files.map((f) => path.join(dir, f)));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Load extensions via DefaultResourceLoader
|
|
192
|
+
const settingsManager = SettingsManager.inMemory();
|
|
193
|
+
const loader = new DefaultResourceLoader({
|
|
194
|
+
cwd: sandboxDir,
|
|
195
|
+
agentDir: sandboxDir,
|
|
196
|
+
settingsManager,
|
|
197
|
+
additionalExtensionPaths: extensionPaths,
|
|
198
|
+
});
|
|
199
|
+
// Extension modules evaluate here; keep the loader configuration in parity
|
|
200
|
+
// with Pi's shipped runtimes (see withoutJitiNativeImport).
|
|
201
|
+
await withoutJitiNativeImport(() => loader.reload());
|
|
202
|
+
const extensionsResult = loader.getExtensions();
|
|
203
|
+
const skillsResult = loader.getSkills();
|
|
204
|
+
// Collect tool names from loaded extensions (no cast needed)
|
|
205
|
+
const toolNames = [];
|
|
206
|
+
for (const ext of extensionsResult.extensions) {
|
|
207
|
+
for (const [name] of ext.tools ?? new Map()) {
|
|
208
|
+
toolNames.push(name);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const result = {
|
|
212
|
+
loaded: {
|
|
213
|
+
extensions: extensionsResult.extensions.length,
|
|
214
|
+
extensionErrors: extensionsResult.errors.map((e) => `${e.path}: ${e.error}`),
|
|
215
|
+
tools: toolNames,
|
|
216
|
+
skills: skillsResult.skills.length,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
// 5. Verify expectations
|
|
220
|
+
if (options.expect) {
|
|
221
|
+
if (options.expect.extensions !== undefined) {
|
|
222
|
+
if (extensionsResult.extensions.length !== options.expect.extensions) {
|
|
223
|
+
throw new Error(`Expected ${options.expect.extensions} extension(s), got ${extensionsResult.extensions.length}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (options.expect.tools) {
|
|
227
|
+
for (const expectedTool of options.expect.tools) {
|
|
228
|
+
if (!toolNames.includes(expectedTool)) {
|
|
229
|
+
throw new Error(`Expected tool "${expectedTool}" not found. Available: ${toolNames.join(", ")}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (options.expect.skills !== undefined) {
|
|
234
|
+
if (skillsResult.skills.length !== options.expect.skills) {
|
|
235
|
+
throw new Error(`Expected ${options.expect.skills} skill(s), got ${skillsResult.skills.length}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// 6. Optional smoke test
|
|
240
|
+
if (options.smoke) {
|
|
241
|
+
const t = await createTestSession({
|
|
242
|
+
extensions: extensionPaths,
|
|
243
|
+
cwd: sandboxDir,
|
|
244
|
+
mockTools: options.smoke.mockTools,
|
|
245
|
+
});
|
|
246
|
+
await t.run(...options.smoke.script);
|
|
247
|
+
result.smoke = { events: t.events };
|
|
248
|
+
t.dispose();
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
// Clean up sandbox (retry for Windows EBUSY on open handles)
|
|
254
|
+
if (fs.existsSync(sandboxDir)) {
|
|
255
|
+
try {
|
|
256
|
+
fs.rmSync(sandboxDir, {
|
|
257
|
+
recursive: true,
|
|
258
|
+
force: true,
|
|
259
|
+
maxRetries: 3,
|
|
260
|
+
retryDelay: 200,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Best-effort cleanup — temp dir will be cleaned by OS
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestSession — orchestrates a test run with playbook, mock tools, and mock UI.
|
|
3
|
+
*
|
|
4
|
+
* 1. Creates a real pi environment (extensions, tools, hooks, session)
|
|
5
|
+
* 2. Replaces streamFn with playbook
|
|
6
|
+
* 3. Intercepts tool.execute() for mockTools
|
|
7
|
+
* 4. Injects mock UI context
|
|
8
|
+
* 5. Collects events
|
|
9
|
+
* 6. Runs conversation script
|
|
10
|
+
*/
|
|
11
|
+
import type { TestSessionOptions, TestSession } from "./types.js";
|
|
12
|
+
export declare function createTestSession(options?: TestSessionOptions): Promise<TestSession>;
|
|
13
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoBH,OAAO,KAAK,EACX,kBAAkB,EAClB,WAAW,EAGX,MAAM,YAAY,CAAC;AAEpB,wBAAsB,iBAAiB,CACtC,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,WAAW,CAAC,CAkNtB"}
|