@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +21 -0
  3. package/README.md +673 -0
  4. package/dist/diagnostics.d.ts +11 -0
  5. package/dist/diagnostics.d.ts.map +1 -0
  6. package/dist/diagnostics.js +61 -0
  7. package/dist/diagnostics.js.map +1 -0
  8. package/dist/events.d.ts +6 -0
  9. package/dist/events.d.ts.map +1 -0
  10. package/dist/events.js +33 -0
  11. package/dist/events.js.map +1 -0
  12. package/dist/index.d.ts +14 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +19 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/mock-pi-script.mjs +176 -0
  17. package/dist/mock-pi.d.ts +32 -0
  18. package/dist/mock-pi.d.ts.map +1 -0
  19. package/dist/mock-pi.js +150 -0
  20. package/dist/mock-pi.js.map +1 -0
  21. package/dist/mock-tools.d.ts +51 -0
  22. package/dist/mock-tools.d.ts.map +1 -0
  23. package/dist/mock-tools.js +192 -0
  24. package/dist/mock-tools.js.map +1 -0
  25. package/dist/mock-ui.d.ts +13 -0
  26. package/dist/mock-ui.d.ts.map +1 -0
  27. package/dist/mock-ui.js +159 -0
  28. package/dist/mock-ui.js.map +1 -0
  29. package/dist/pi-loader-parity.d.ts +36 -0
  30. package/dist/pi-loader-parity.d.ts.map +1 -0
  31. package/dist/pi-loader-parity.js +60 -0
  32. package/dist/pi-loader-parity.js.map +1 -0
  33. package/dist/playbook.d.ts +44 -0
  34. package/dist/playbook.d.ts.map +1 -0
  35. package/dist/playbook.js +143 -0
  36. package/dist/playbook.js.map +1 -0
  37. package/dist/sandbox.d.ts +27 -0
  38. package/dist/sandbox.d.ts.map +1 -0
  39. package/dist/sandbox.js +269 -0
  40. package/dist/sandbox.js.map +1 -0
  41. package/dist/session.d.ts +13 -0
  42. package/dist/session.d.ts.map +1 -0
  43. package/dist/session.js +187 -0
  44. package/dist/session.js.map +1 -0
  45. package/dist/types.d.ts +171 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +5 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/utils.d.ts +32 -0
  50. package/dist/utils.d.ts.map +1 -0
  51. package/dist/utils.js +46 -0
  52. package/dist/utils.js.map +1 -0
  53. package/package.json +84 -0
  54. package/skills/pi-test-harness/SKILL.md +451 -0
  55. package/skills/pi-test-harness/evals/evals.json +26 -0
  56. package/skills/pi-test-harness/references/api-reference.md +480 -0
  57. package/skills/pi-test-harness/references/mock-pi-cli.md +135 -0
  58. package/skills/pi-test-harness/references/mock-tools.md +176 -0
  59. package/skills/pi-test-harness/references/mock-ui.md +170 -0
  60. package/skills/pi-test-harness/references/playbook-dsl.md +209 -0
  61. package/skills/pi-test-harness/references/sandbox-install.md +113 -0
  62. package/src/diagnostics.ts +90 -0
  63. package/src/events.ts +43 -0
  64. package/src/index.ts +42 -0
  65. package/src/mock-pi-script.mjs +176 -0
  66. package/src/mock-pi.ts +169 -0
  67. package/src/mock-tools.ts +252 -0
  68. package/src/mock-ui.ts +196 -0
  69. package/src/pi-loader-parity.ts +61 -0
  70. package/src/playbook.ts +189 -0
  71. package/src/sandbox.ts +334 -0
  72. package/src/session.ts +249 -0
  73. package/src/types.ts +203 -0
  74. package/src/utils.ts +46 -0
@@ -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":"AAAA;;;;;GAKG;AAEH,OAAO,EACN,iCAAiC,GAMjC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,+DAA+D;AAE/D,oCAAoC;AACpC,MAAM,UAAU;IACN,MAAM,CAAiB;IAEhC,YAAY,QAAgB,EAAE,MAAiE;QAC9F,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,QAA4C;QAChD,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,QAAQ,CAAC;QACpC,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CACpB,QAAgB,EAChB,SAAoE,EAAE;IAEtE,OAAO,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,IAAY;IAChC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAAC,MAAc,EAAE,OAA2C;IAC/E,OAAO;QACN,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrE,CAAC;AACH,CAAC;AAED,+DAA+D;AAE/D,SAAS,aAAa,CAAC,MAA6E;IACnG,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,IAAI,OAAO,MAAM,KAAK,UAAU;QAAE,OAAO,MAAM,EAAE,CAAC;IAClD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAsB,EAAE,eAAuB;IAC9E,MAAM,OAAO,GAAgC,EAAE,CAAC;IAEhD,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,eAAe,eAAe,EAAE;YACpC,IAAI,EAAE,MAAM,CAAC,QAAS;YACtB,SAAS,EAAE,cAAc;SACzB,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,OAAO;QACP,GAAG,EAAE,kBAAkB;QACvB,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,UAAU;QACjB,KAAK,EAAE;YACN,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC;YACZ,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;SACpE;QACD,UAAU,EAAE,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;QACvD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACrB,CAAC;AACH,CAAC;AAWD,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAInD,+CAA+C;IAC/C,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,KAAK,GAAkB;QAC5B,QAAQ,EAAE,CAAC;QACX,SAAS,EAAE,KAAK,CAAC,MAAM;QACvB,eAAe,EAAE,EAAE;QACnB,gBAAgB,EAAE,IAAI,GAAG,EAAE;KAC3B,CAAC;IAEF,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,MAAM,QAAQ,GAAG,CAChB,MAAkB,EAClB,QAAiB,EACjB,QAA8B,EACA,EAAE;QAChC,MAAM,MAAM,GAAG,iCAAiC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAE7B,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,qBAAqB;YACrB,MAAM,UAAU,GAAG,wBAAwB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YAChE,MAAM,QAAQ,GAAqB;gBAClC,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,UAAU,EAAE,EAAE,CAAC;gBACvE,GAAG,EAAE,kBAAkB;gBACvB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;gBACjJ,UAAU,EAAE,MAAM;gBAClB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACrB,CAAC;YACF,cAAc,CAAC,GAAG,EAAE;gBACnB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QACf,CAAC;QAED,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjB,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/B,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEnC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;YAAE,eAAe,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAEhE,sEAAsE;QACtE,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACnD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;YACrE,MAAM,IAAI,GAAG,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC,CAAE,SAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAS,CAAC;YACvF,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QACvD,CAAC;QAED,cAAc,CAAC,GAAG,EAAE;YACnB,MAAM,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;gBAC7D,OAAO;aACP,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;IAEF,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC"}
@@ -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"}
@@ -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"}