@introspection-ai/recipes 0.13.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/LICENSE +191 -0
- package/README.md +184 -0
- package/dist/agent-tool.d.ts +42 -0
- package/dist/agent-tool.d.ts.map +1 -0
- package/dist/agent-tool.js +159 -0
- package/dist/agent-tool.js.map +1 -0
- package/dist/child-agent-completions.d.ts +61 -0
- package/dist/child-agent-completions.d.ts.map +1 -0
- package/dist/child-agent-completions.js +135 -0
- package/dist/child-agent-completions.js.map +1 -0
- package/dist/child-agent-store.d.ts +48 -0
- package/dist/child-agent-store.d.ts.map +1 -0
- package/dist/child-agent-store.js +98 -0
- package/dist/child-agent-store.js.map +1 -0
- package/dist/child-agent.d.ts +43 -0
- package/dist/child-agent.d.ts.map +1 -0
- package/dist/child-agent.js +268 -0
- package/dist/child-agent.js.map +1 -0
- package/dist/direct-cli.d.ts +9 -0
- package/dist/direct-cli.d.ts.map +1 -0
- package/dist/direct-cli.js +22 -0
- package/dist/direct-cli.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect.d.ts +26 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +70 -0
- package/dist/inspect.js.map +1 -0
- package/dist/interactions.d.ts +204 -0
- package/dist/interactions.d.ts.map +1 -0
- package/dist/interactions.js +358 -0
- package/dist/interactions.js.map +1 -0
- package/dist/mcp-catalog.d.ts +8 -0
- package/dist/mcp-catalog.d.ts.map +1 -0
- package/dist/mcp-catalog.js +60 -0
- package/dist/mcp-catalog.js.map +1 -0
- package/dist/mcp-cli-core.d.ts +79 -0
- package/dist/mcp-cli-core.d.ts.map +1 -0
- package/dist/mcp-cli-core.js +1717 -0
- package/dist/mcp-cli-core.js.map +1 -0
- package/dist/mcp-cli-help.d.ts +2 -0
- package/dist/mcp-cli-help.d.ts.map +1 -0
- package/dist/mcp-cli-help.js +36 -0
- package/dist/mcp-cli-help.js.map +1 -0
- package/dist/mcp-cli-policy.d.ts +17 -0
- package/dist/mcp-cli-policy.d.ts.map +1 -0
- package/dist/mcp-cli-policy.js +230 -0
- package/dist/mcp-cli-policy.js.map +1 -0
- package/dist/mcp-cli.d.ts +3 -0
- package/dist/mcp-cli.d.ts.map +1 -0
- package/dist/mcp-cli.js +33 -0
- package/dist/mcp-cli.js.map +1 -0
- package/dist/mcp-client.d.ts +3 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +86 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/mcp-command-context.d.ts +13 -0
- package/dist/mcp-command-context.d.ts.map +1 -0
- package/dist/mcp-command-context.js +29 -0
- package/dist/mcp-command-context.js.map +1 -0
- package/dist/mcp-contract.d.ts +16 -0
- package/dist/mcp-contract.d.ts.map +1 -0
- package/dist/mcp-contract.js +306 -0
- package/dist/mcp-contract.js.map +1 -0
- package/dist/mcp-daemon-client.d.ts +10 -0
- package/dist/mcp-daemon-client.d.ts.map +1 -0
- package/dist/mcp-daemon-client.js +119 -0
- package/dist/mcp-daemon-client.js.map +1 -0
- package/dist/mcp-daemon-protocol.d.ts +64 -0
- package/dist/mcp-daemon-protocol.d.ts.map +1 -0
- package/dist/mcp-daemon-protocol.js +6 -0
- package/dist/mcp-daemon-protocol.js.map +1 -0
- package/dist/mcp-daemon.d.ts +3 -0
- package/dist/mcp-daemon.d.ts.map +1 -0
- package/dist/mcp-daemon.js +76880 -0
- package/dist/mcp-daemon.js.map +1 -0
- package/dist/mcp-run-worker.d.ts +2 -0
- package/dist/mcp-run-worker.d.ts.map +1 -0
- package/dist/mcp-run-worker.js +76579 -0
- package/dist/mcp-run-worker.js.map +1 -0
- package/dist/mcp.d.ts +168 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +558 -0
- package/dist/mcp.js.map +1 -0
- package/dist/pi-extension.d.ts +30 -0
- package/dist/pi-extension.d.ts.map +1 -0
- package/dist/pi-extension.js +880 -0
- package/dist/pi-extension.js.map +1 -0
- package/dist/provider-env.d.ts +2 -0
- package/dist/provider-env.d.ts.map +1 -0
- package/dist/provider-env.js +21 -0
- package/dist/provider-env.js.map +1 -0
- package/dist/recipe/resolve.d.ts +30 -0
- package/dist/recipe/resolve.d.ts.map +1 -0
- package/dist/recipe/resolve.js +131 -0
- package/dist/recipe/resolve.js.map +1 -0
- package/dist/recipe-agent.d.ts +71 -0
- package/dist/recipe-agent.d.ts.map +1 -0
- package/dist/recipe-agent.js +555 -0
- package/dist/recipe-agent.js.map +1 -0
- package/dist/recipe-extensions.d.ts +9 -0
- package/dist/recipe-extensions.d.ts.map +1 -0
- package/dist/recipe-extensions.js +81 -0
- package/dist/recipe-extensions.js.map +1 -0
- package/dist/recipe-mcp-config.d.ts +15 -0
- package/dist/recipe-mcp-config.d.ts.map +1 -0
- package/dist/recipe-mcp-config.js +82 -0
- package/dist/recipe-mcp-config.js.map +1 -0
- package/dist/recipe-model.d.ts +49 -0
- package/dist/recipe-model.d.ts.map +1 -0
- package/dist/recipe-model.js +299 -0
- package/dist/recipe-model.js.map +1 -0
- package/dist/recipe-package.d.ts +79 -0
- package/dist/recipe-package.d.ts.map +1 -0
- package/dist/recipe-package.js +479 -0
- package/dist/recipe-package.js.map +1 -0
- package/dist/recipe-skills.d.ts +7 -0
- package/dist/recipe-skills.d.ts.map +1 -0
- package/dist/recipe-skills.js +46 -0
- package/dist/recipe-skills.js.map +1 -0
- package/dist/run-controller.d.ts +32 -0
- package/dist/run-controller.d.ts.map +1 -0
- package/dist/run-controller.js +259 -0
- package/dist/run-controller.js.map +1 -0
- package/dist/run.d.ts +28 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +84 -0
- package/dist/run.js.map +1 -0
- package/dist/session.d.ts +140 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +363 -0
- package/dist/session.js.map +1 -0
- package/dist/test-utils.d.ts +32 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +259 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/testing.d.ts +25 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +96 -0
- package/dist/testing.js.map +1 -0
- package/docs/agent-composition.md +196 -0
- package/docs/deployment-configuration.md +65 -0
- package/docs/index.md +49 -0
- package/docs/interactions.md +210 -0
- package/docs/mcp-auth.md +80 -0
- package/docs/mcp-configuration.md +126 -0
- package/docs/migration.md +70 -0
- package/docs/pi-extension.md +97 -0
- package/docs/python-bindings-release.md +47 -0
- package/docs/recipe-evals.md +70 -0
- package/docs/recipe-flow.md +41 -0
- package/docs/recipe-format.md +186 -0
- package/docs/recipe-judges.md +152 -0
- package/docs/runtime-library.md +171 -0
- package/package.json +106 -0
- package/vendor/mcp-client/darwin-arm64/mcp-client +0 -0
- package/vendor/mcp-client/darwin-x64/mcp-client +0 -0
- package/vendor/mcp-client/linux-arm64/mcp-client +0 -0
- package/vendor/mcp-client/linux-x64/mcp-client +0 -0
package/dist/session.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
|
3
|
+
import { getEnvApiKey, getModel } from "@earendil-works/pi-ai/compat";
|
|
4
|
+
import { createAgentSessionFromServices, createAgentSessionServices, ModelRuntime, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { instrumentSession, } from "@introspection-sdk/introspection-pi";
|
|
6
|
+
import { createAgentTool } from "./agent-tool.js";
|
|
7
|
+
import { clearMcpCatalogPreload, preloadMcpCatalogs, } from "./mcp-catalog.js";
|
|
8
|
+
import { configureMcpLocalConfigPath, isolateMcpEnvironment, materializeMcpSession, materializeSessionMcpCli, restoreMcpEnvironment, resolveAgentMcpSelections, snapshotMcpEnvironment, stopMcpDaemon, } from "./mcp.js";
|
|
9
|
+
import { expectedProviderEnvVars } from "./provider-env.js";
|
|
10
|
+
import { loadRecipeExtensionFactory } from "./recipe-extensions.js";
|
|
11
|
+
import { applyRecipeAgentModelConfigToModel, applyRecipeAgentModelConfigToSession, } from "./recipe-model.js";
|
|
12
|
+
import { resolveRecipe } from "./recipe/resolve.js";
|
|
13
|
+
export { expectedProviderEnvVars } from "./provider-env.js";
|
|
14
|
+
/** The session model's provider has no resolvable credential. */
|
|
15
|
+
export class RecipeCredentialError extends Error {
|
|
16
|
+
provider;
|
|
17
|
+
expectedEnvVars;
|
|
18
|
+
name = "RecipeCredentialError";
|
|
19
|
+
constructor(provider,
|
|
20
|
+
/** Env vars that would satisfy the provider, most conventional first. */
|
|
21
|
+
expectedEnvVars) {
|
|
22
|
+
super(`No credential for model provider "${provider}": set ${expectedEnvVars.join(" or ")}, or pass a CredentialStore via credentials`);
|
|
23
|
+
this.provider = provider;
|
|
24
|
+
this.expectedEnvVars = expectedEnvVars;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** The recipe's model spec does not resolve to a known model. */
|
|
28
|
+
export class RecipeModelError extends Error {
|
|
29
|
+
modelSpec;
|
|
30
|
+
name = "RecipeModelError";
|
|
31
|
+
constructor(modelSpec) {
|
|
32
|
+
super(`Recipe model is not available: ${modelSpec}`);
|
|
33
|
+
this.modelSpec = modelSpec;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function parseModelSpec(spec) {
|
|
37
|
+
const slash = spec.indexOf("/");
|
|
38
|
+
if (slash < 0) {
|
|
39
|
+
throw new Error(`Invalid recipe model "${spec}" - expected "<provider>/<model_id>"`);
|
|
40
|
+
}
|
|
41
|
+
const provider = spec.slice(0, slash);
|
|
42
|
+
const modelId = spec.slice(slash + 1);
|
|
43
|
+
const lookupProvider = provider === "gemini" ? "google" : provider;
|
|
44
|
+
return { provider, modelId, lookupProvider };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Ambient-credential providers resolve from their own SDK chain (AWS
|
|
48
|
+
* profiles, ADC); `getEnvApiKey` reports them as "<authenticated>".
|
|
49
|
+
*/
|
|
50
|
+
const AMBIENT_CREDENTIAL_SENTINEL = "<authenticated>";
|
|
51
|
+
/**
|
|
52
|
+
* Resolve credentials fail-closed against the session's own `env`, never the
|
|
53
|
+
* ambient process env: explicit store → per-provider env keys → error.
|
|
54
|
+
*/
|
|
55
|
+
async function resolveCredentialStore(lookupProvider, opts) {
|
|
56
|
+
if (opts.credentials) {
|
|
57
|
+
const stored = await opts.credentials.read(lookupProvider);
|
|
58
|
+
if (!stored) {
|
|
59
|
+
throw new RecipeCredentialError(lookupProvider, expectedProviderEnvVars(lookupProvider));
|
|
60
|
+
}
|
|
61
|
+
return opts.credentials;
|
|
62
|
+
}
|
|
63
|
+
const env = opts.env ?? process.env;
|
|
64
|
+
const store = new InMemoryCredentialStore();
|
|
65
|
+
const apiKey = getEnvApiKey(lookupProvider, env) ??
|
|
66
|
+
env[`${lookupProvider.toUpperCase().replace(/-/g, "_")}_API_KEY`];
|
|
67
|
+
if (!apiKey) {
|
|
68
|
+
throw new RecipeCredentialError(lookupProvider, expectedProviderEnvVars(lookupProvider));
|
|
69
|
+
}
|
|
70
|
+
if (apiKey !== AMBIENT_CREDENTIAL_SENTINEL) {
|
|
71
|
+
await store.modify(lookupProvider, async () => ({
|
|
72
|
+
type: "api_key",
|
|
73
|
+
key: apiKey,
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
// Ambient-credential providers (Bedrock, Vertex ADC) keep an empty store;
|
|
77
|
+
// the pi-ai provider resolves its own credential chain at stream time.
|
|
78
|
+
return store;
|
|
79
|
+
}
|
|
80
|
+
function scopedMcpSelections(recipe) {
|
|
81
|
+
return [recipe.agent, ...recipe.subagents.values()].flatMap((agent) => resolveAgentMcpSelections(agent.mcp));
|
|
82
|
+
}
|
|
83
|
+
const leasedMcpEnvironments = new WeakSet();
|
|
84
|
+
export class RecipeMcpEnvironmentInUseError extends Error {
|
|
85
|
+
name = "RecipeMcpEnvironmentInUseError";
|
|
86
|
+
constructor() {
|
|
87
|
+
super("This environment already belongs to a live materialized Recipe MCP session; use a separate env object per concurrent session or let the host materialize MCP once and pass mcpMode: \"inherit\"");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Everything `createRecipeSession` fail-closes on, without creating a
|
|
92
|
+
* session or starting MCP: recipe resolution, model lookup, credentials.
|
|
93
|
+
* Hosts with a boot phase (the serve layer) run this once to fail fast.
|
|
94
|
+
*/
|
|
95
|
+
export async function preflightRecipeSession(opts) {
|
|
96
|
+
const recipe = resolveRecipe({
|
|
97
|
+
recipeDir: opts.recipeDir,
|
|
98
|
+
...(opts.agentName ? { agentName: opts.agentName } : {}),
|
|
99
|
+
});
|
|
100
|
+
const modelSpec = opts.model ?? recipe.modelSpec;
|
|
101
|
+
const { lookupProvider, modelId } = parseModelSpec(modelSpec);
|
|
102
|
+
const credentialProvider = opts.modelOverride?.provider ?? lookupProvider;
|
|
103
|
+
const credentials = await resolveCredentialStore(credentialProvider, opts);
|
|
104
|
+
const modelRuntime = await ModelRuntime.create({
|
|
105
|
+
credentials,
|
|
106
|
+
modelsPath: null,
|
|
107
|
+
});
|
|
108
|
+
const model = opts.modelOverride ??
|
|
109
|
+
modelRuntime.getModel(lookupProvider, modelId) ??
|
|
110
|
+
getModel(lookupProvider, modelId);
|
|
111
|
+
if (!model) {
|
|
112
|
+
throw new RecipeModelError(modelSpec);
|
|
113
|
+
}
|
|
114
|
+
return recipe;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Materialize the session MCP runtime for a recipe scope into `env` at
|
|
118
|
+
* `cwd`. Exposed for hosts that materialize once per process (the serve
|
|
119
|
+
* layer) and create their sessions with `mcpMode: "inherit"`.
|
|
120
|
+
*/
|
|
121
|
+
export async function materializeRecipeSessionMcp(recipe, cwd, env, opts = {}) {
|
|
122
|
+
return configureSessionMcp(recipe, cwd, env, {
|
|
123
|
+
recipeDir: recipe.recipeDir,
|
|
124
|
+
...opts,
|
|
125
|
+
}, false);
|
|
126
|
+
}
|
|
127
|
+
async function configureSessionMcp(recipe, cwd, env, opts, leaseEnvironment) {
|
|
128
|
+
const selections = scopedMcpSelections(recipe);
|
|
129
|
+
if (opts.mcpMode === "inherit")
|
|
130
|
+
return { materialized: false };
|
|
131
|
+
if (selections.length === 0)
|
|
132
|
+
return { materialized: false };
|
|
133
|
+
const snapshot = leaseEnvironment ? snapshotMcpEnvironment(env) : undefined;
|
|
134
|
+
if (leaseEnvironment) {
|
|
135
|
+
if (leasedMcpEnvironments.has(env)) {
|
|
136
|
+
throw new RecipeMcpEnvironmentInUseError();
|
|
137
|
+
}
|
|
138
|
+
leasedMcpEnvironments.add(env);
|
|
139
|
+
isolateMcpEnvironment(env);
|
|
140
|
+
}
|
|
141
|
+
const release = async () => {
|
|
142
|
+
if (!snapshot)
|
|
143
|
+
return;
|
|
144
|
+
try {
|
|
145
|
+
clearMcpCatalogPreload(env);
|
|
146
|
+
await stopMcpDaemon(env);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
restoreMcpEnvironment(env, snapshot);
|
|
150
|
+
leasedMcpEnvironments.delete(env);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
try {
|
|
154
|
+
if (opts.mcpBindingsPath) {
|
|
155
|
+
env.PI_RECIPES_MCP_LOCAL_CONFIG = opts.mcpBindingsPath;
|
|
156
|
+
}
|
|
157
|
+
else if (!opts.mcpBindings) {
|
|
158
|
+
configureMcpLocalConfigPath({ cwd, recipeDir: recipe.recipeDir, env });
|
|
159
|
+
}
|
|
160
|
+
const [cliResult, sessionResult] = await Promise.allSettled([
|
|
161
|
+
materializeSessionMcpCli({ cwd, env }),
|
|
162
|
+
materializeMcpSession({
|
|
163
|
+
cwd,
|
|
164
|
+
manifest: recipe.manifest,
|
|
165
|
+
agentMcp: selections,
|
|
166
|
+
env,
|
|
167
|
+
...(opts.mcpBindings ? { localConfig: opts.mcpBindings } : {}),
|
|
168
|
+
}),
|
|
169
|
+
]);
|
|
170
|
+
if (cliResult.status === "rejected")
|
|
171
|
+
throw cliResult.reason;
|
|
172
|
+
if (sessionResult.status === "rejected")
|
|
173
|
+
throw sessionResult.reason;
|
|
174
|
+
const session = sessionResult.value;
|
|
175
|
+
if (session.servers.length > 0) {
|
|
176
|
+
// Warm tool catalogs in the background; sessions work without the warmup.
|
|
177
|
+
void preloadMcpCatalogs({ env }).catch(() => { });
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
materialized: true,
|
|
181
|
+
...(snapshot ? { release } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
await release();
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export async function createRecipeSession(opts) {
|
|
190
|
+
const recipe = resolveRecipe({
|
|
191
|
+
recipeDir: opts.recipeDir,
|
|
192
|
+
...(opts.agentName ? { agentName: opts.agentName } : {}),
|
|
193
|
+
});
|
|
194
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
195
|
+
const env = opts.env ?? process.env;
|
|
196
|
+
const otel = opts.otel
|
|
197
|
+
? {
|
|
198
|
+
...opts.otel,
|
|
199
|
+
meta: {
|
|
200
|
+
conversationId: opts.otel.meta?.conversationId ?? randomUUID(),
|
|
201
|
+
agentId: opts.otel.meta?.agentId ??
|
|
202
|
+
`${recipe.manifest.name}/${recipe.agentName}`,
|
|
203
|
+
agentName: opts.otel.meta?.agentName ?? recipe.agentName,
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
: undefined;
|
|
207
|
+
// Model + credentials, fail-closed before any MCP runtime starts.
|
|
208
|
+
const modelSpec = opts.model ?? recipe.modelSpec;
|
|
209
|
+
const { lookupProvider, modelId } = parseModelSpec(modelSpec);
|
|
210
|
+
const credentialProvider = opts.modelOverride?.provider ?? lookupProvider;
|
|
211
|
+
const credentials = await resolveCredentialStore(credentialProvider, opts);
|
|
212
|
+
const modelRuntime = await ModelRuntime.create({ credentials, modelsPath: null });
|
|
213
|
+
const model = opts.modelOverride ??
|
|
214
|
+
modelRuntime.getModel(lookupProvider, modelId) ??
|
|
215
|
+
getModel(lookupProvider, modelId);
|
|
216
|
+
if (!model) {
|
|
217
|
+
throw new RecipeModelError(modelSpec);
|
|
218
|
+
}
|
|
219
|
+
applyRecipeAgentModelConfigToModel(model, recipe.modelConfig);
|
|
220
|
+
const mcp = await configureSessionMcp(recipe, cwd, env, opts, true);
|
|
221
|
+
let session;
|
|
222
|
+
let runs;
|
|
223
|
+
let unsubscribe;
|
|
224
|
+
let detachInstrumentation;
|
|
225
|
+
const cleanup = async () => {
|
|
226
|
+
try {
|
|
227
|
+
unsubscribe?.();
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// Continue releasing the remaining session-owned resources.
|
|
231
|
+
}
|
|
232
|
+
unsubscribe = undefined;
|
|
233
|
+
if (runs) {
|
|
234
|
+
try {
|
|
235
|
+
for (const run of runs.list()) {
|
|
236
|
+
if (run.status !== "closed") {
|
|
237
|
+
await runs.close(run.agent_run_id).catch(() => { });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Child teardown is best-effort; the session still disposes.
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (session) {
|
|
246
|
+
try {
|
|
247
|
+
await session.abort();
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// An idle session has nothing to abort.
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
session.dispose();
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Continue releasing instrumentation and MCP state.
|
|
257
|
+
}
|
|
258
|
+
session = undefined;
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
detachInstrumentation?.();
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Continue releasing MCP state.
|
|
265
|
+
}
|
|
266
|
+
detachInstrumentation = undefined;
|
|
267
|
+
await mcp.release?.();
|
|
268
|
+
};
|
|
269
|
+
try {
|
|
270
|
+
// Recipe extensions load through the shared jiti loader; host extensions
|
|
271
|
+
// append after them, matching the Pi extension host's ordering.
|
|
272
|
+
const inlineExtensions = [];
|
|
273
|
+
for (const extensionPath of recipe.extensionPaths) {
|
|
274
|
+
inlineExtensions.push(await loadRecipeExtensionFactory(recipe.recipeDir, extensionPath));
|
|
275
|
+
}
|
|
276
|
+
inlineExtensions.push(...(opts.extensionFactories ?? []));
|
|
277
|
+
const services = await createAgentSessionServices({
|
|
278
|
+
cwd,
|
|
279
|
+
agentDir: recipe.recipeDir,
|
|
280
|
+
modelRuntime,
|
|
281
|
+
settingsManager: opts.settingsManager ?? SettingsManager.create(cwd, recipe.recipeDir),
|
|
282
|
+
resourceLoaderOptions: {
|
|
283
|
+
eventBus: opts.eventBus,
|
|
284
|
+
noSkills: true,
|
|
285
|
+
additionalSkillPaths: [
|
|
286
|
+
...(opts.skillPaths ?? recipe.skillPaths),
|
|
287
|
+
...(opts.additionalSkillPaths ?? []),
|
|
288
|
+
],
|
|
289
|
+
additionalPromptTemplatePaths: recipe.promptPaths,
|
|
290
|
+
extensionFactories: inlineExtensions,
|
|
291
|
+
systemPromptOverride: (base) => {
|
|
292
|
+
const resolved = recipe.systemPromptOverride(base);
|
|
293
|
+
return opts.systemPrompt ? opts.systemPrompt(resolved ?? "") : resolved;
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
opts.onDiagnostics?.(services.diagnostics);
|
|
298
|
+
// Subagents: the shared `agent` tool against an injected or in-process
|
|
299
|
+
// controller. `runController: null` disables delegation outright.
|
|
300
|
+
const wantsSubagents = recipe.subagents.size > 0 && opts.runController !== null;
|
|
301
|
+
if (opts.runController) {
|
|
302
|
+
runs = opts.runController;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
const { createInProcessRunController, inertRunController } = await import("./run-controller.js");
|
|
306
|
+
runs = wantsSubagents
|
|
307
|
+
? createInProcessRunController({
|
|
308
|
+
recipeDir: recipe.recipeDir,
|
|
309
|
+
cwd,
|
|
310
|
+
env,
|
|
311
|
+
...(opts.credentials ? { credentials: opts.credentials } : {}),
|
|
312
|
+
concurrency: opts.subagentLimits?.concurrency,
|
|
313
|
+
...(otel ? { otel } : {}),
|
|
314
|
+
})
|
|
315
|
+
: inertRunController();
|
|
316
|
+
}
|
|
317
|
+
const tools = wantsSubagents
|
|
318
|
+
? recipe.tools
|
|
319
|
+
: recipe.tools.filter((tool) => tool !== "agent");
|
|
320
|
+
const customTools = [
|
|
321
|
+
...(opts.customTools ?? []),
|
|
322
|
+
...(wantsSubagents
|
|
323
|
+
? [createAgentTool(runs, recipe.subagents, opts.agentToolOptions)]
|
|
324
|
+
: []),
|
|
325
|
+
];
|
|
326
|
+
const created = await createAgentSessionFromServices({
|
|
327
|
+
services,
|
|
328
|
+
sessionManager: opts.sessionManager ?? SessionManager.inMemory(cwd),
|
|
329
|
+
model,
|
|
330
|
+
...(opts.thinkingLevel ?? recipe.thinkingLevel
|
|
331
|
+
? { thinkingLevel: (opts.thinkingLevel ?? recipe.thinkingLevel) }
|
|
332
|
+
: {}),
|
|
333
|
+
tools,
|
|
334
|
+
customTools,
|
|
335
|
+
});
|
|
336
|
+
session = created.session;
|
|
337
|
+
applyRecipeAgentModelConfigToSession(session, recipe.modelConfig);
|
|
338
|
+
await session.bindExtensions({});
|
|
339
|
+
unsubscribe = opts.onEvent ? session.subscribe(opts.onEvent) : undefined;
|
|
340
|
+
detachInstrumentation = otel
|
|
341
|
+
? instrumentSession(session, otel).detach
|
|
342
|
+
: undefined;
|
|
343
|
+
const liveSession = session;
|
|
344
|
+
const liveRuns = runs;
|
|
345
|
+
let disposed = false;
|
|
346
|
+
return {
|
|
347
|
+
session: liveSession,
|
|
348
|
+
recipe,
|
|
349
|
+
runs: liveRuns,
|
|
350
|
+
async dispose() {
|
|
351
|
+
if (disposed)
|
|
352
|
+
return;
|
|
353
|
+
disposed = true;
|
|
354
|
+
await cleanup();
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
await cleanup();
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAwB,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAc,MAAM,8BAA8B,CAAC;AAElF,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,YAAY,EACZ,cAAc,EACd,eAAe,GAQhB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,iBAAiB,GAGlB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,eAAe,EAA2B,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EACL,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,GAGd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,kCAAkC,EAClC,oCAAoC,GACrC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAuB,MAAM,qBAAqB,CAAC;AAEzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAmG5D,iEAAiE;AACjE,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAInC;IAEA;IALO,IAAI,GAAG,uBAAuB,CAAC;IAEjD,YACW,QAAgB;IACzB,yEAAyE;IAChE,eAAkC;QAE3C,KAAK,CACH,qCAAqC,QAAQ,UAAU,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,6CAA6C,CACjI,CAAC;QANO,aAAQ,GAAR,QAAQ,CAAQ;QAEhB,oBAAe,GAAf,eAAe,CAAmB;IAK7C,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAGpB;IAFH,IAAI,GAAG,kBAAkB,CAAC;IAE5C,YAAqB,SAAiB;QACpC,KAAK,CAAC,kCAAkC,SAAS,EAAE,CAAC,CAAC;QADlC,cAAS,GAAT,SAAS,CAAQ;IAEtC,CAAC;CACF;AAED,SAAS,cAAc,CAAC,IAAY;IAKlC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,sCAAsC,CACpE,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACtC,MAAM,cAAc,GAAG,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IACnE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,2BAA2B,GAAG,iBAAiB,CAAC;AAEtD;;;GAGG;AACH,KAAK,UAAU,sBAAsB,CACnC,cAAsB,EACtB,IAAgC;IAEhC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,qBAAqB,CAC7B,cAAc,EACd,uBAAuB,CAAC,cAAc,CAAC,CACxC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,uBAAuB,EAAE,CAAC;IAC5C,MAAM,MAAM,GACV,YAAY,CAAC,cAAc,EAAE,GAA6B,CAAC;QAC3D,GAAG,CAAC,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACpE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,qBAAqB,CAC7B,cAAc,EACd,uBAAuB,CAAC,cAAc,CAAC,CACxC,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,KAAK,2BAA2B,EAAE,CAAC;QAC3C,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YAC9C,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,MAAM;SACZ,CAAC,CAAC,CAAC;IACN,CAAC;IACD,0EAA0E;IAC1E,uEAAuE;IACvE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAsB;IACjD,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACpE,yBAAyB,CAAC,KAAK,CAAC,GAAG,CAAC,CACrC,CAAC;AACJ,CAAC;AAOD,MAAM,qBAAqB,GAAG,IAAI,OAAO,EAAqB,CAAC;AAE/D,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IACrC,IAAI,GAAG,gCAAgC,CAAC;IAE1D;QACE,KAAK,CACH,iMAAiM,CAClM,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAgC;IAEhC,MAAM,MAAM,GAAG,aAAa,CAAC;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzD,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC;IACjD,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,IAAI,cAAc,CAAC;IAC1E,MAAM,WAAW,GAAG,MAAM,sBAAsB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IAC3E,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC;QAC7C,WAAW;QACX,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IACH,MAAM,KAAK,GACT,IAAI,CAAC,aAAa;QAClB,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;QAC7C,QAAQ,CAAC,cAAuB,EAAE,OAAgB,CAErC,CAAC;IACjB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAsB,EACtB,GAAW,EACX,GAAsB,EACtB,OAGI,EAAE;IAEN,OAAO,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE;QAC3C,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,IAAI;KACR,EAAE,KAAK,CAAC,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,MAAsB,EACtB,GAAW,EACX,GAAsB,EACtB,IAAgC,EAChC,gBAAyB;IAEzB,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAC/D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAE5D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,IAAI,gBAAgB,EAAE,CAAC;QACrB,IAAI,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,8BAA8B,EAAE,CAAC;QAC7C,CAAC;QACD,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;QACzB,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,IAAI,CAAC;YACH,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACT,qBAAqB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACrC,qBAAqB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,GAAG,CAAC,2BAA2B,GAAG,IAAI,CAAC,eAAe,CAAC;QACzD,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7B,2BAA2B,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YAC1D,wBAAwB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;YACtC,qBAAqB,CAAC;gBACpB,GAAG;gBACH,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ,EAAE,UAAU;gBACpB,GAAG;gBACH,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC/D,CAAC;SACH,CAAC,CAAC;QACH,IAAI,SAAS,CAAC,MAAM,KAAK,UAAU;YAAE,MAAM,SAAS,CAAC,MAAM,CAAC;QAC5D,IAAI,aAAa,CAAC,MAAM,KAAK,UAAU;YAAE,MAAM,aAAa,CAAC,MAAM,CAAC;QACpE,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC;QACpC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,0EAA0E;YAC1E,KAAK,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnD,CAAC;QACD,OAAO;YACL,YAAY,EAAE,IAAI;YAClB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAgC;IAEhC,MAAM,MAAM,GAAG,aAAa,CAAC;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzD,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI;QACpB,CAAC,CAAC;YACE,GAAG,IAAI,CAAC,IAAI;YACZ,IAAI,EAAE;gBACJ,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,IAAI,UAAU,EAAE;gBAC9D,OAAO,EACL,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO;oBACvB,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,MAAM,CAAC,SAAS,EAAE;gBAC/C,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,MAAM,CAAC,SAAS;aACzD;SACF;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,kEAAkE;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC;IACjD,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,IAAI,cAAc,CAAC;IAC1E,MAAM,WAAW,GAAG,MAAM,sBAAsB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IAC3E,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAClF,MAAM,KAAK,GACT,IAAI,CAAC,aAAa;QAClB,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;QAC7C,QAAQ,CAAC,cAAuB,EAAE,OAAgB,CAA4B,CAAC;IAClF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IACD,kCAAkC,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAE9D,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACpE,IAAI,OAAiC,CAAC;IACtC,IAAI,IAAoC,CAAC;IACzC,IAAI,WAAqC,CAAC;IAC1C,IAAI,qBAA+C,CAAC;IAEpD,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;QACxC,IAAI,CAAC;YACH,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;QACD,WAAW,GAAG,SAAS,CAAC;QACxB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC;gBACH,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;oBAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;wBAC5B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACrD,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;YAC/D,CAAC;QACH,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,wCAAwC;YAC1C,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,oDAAoD;YACtD,CAAC;YACD,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC;QACD,IAAI,CAAC;YACH,qBAAqB,EAAE,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,gCAAgC;QAClC,CAAC;QACD,qBAAqB,GAAG,SAAS,CAAC;QAClC,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;IACxB,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,yEAAyE;QACzE,gEAAgE;QAChE,MAAM,gBAAgB,GAAsB,EAAE,CAAC;QAC/C,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAClD,gBAAgB,CAAC,IAAI,CACnB,MAAM,0BAA0B,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAClE,CAAC;QACJ,CAAC;QACD,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,CAAC;QAE1D,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC;YAChD,GAAG;YACH,QAAQ,EAAE,MAAM,CAAC,SAAS;YAC1B,YAAY;YACZ,eAAe,EACb,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC;YACvE,qBAAqB,EAAE;gBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI;gBACd,oBAAoB,EAAE;oBACpB,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;oBACzC,GAAG,CAAC,IAAI,CAAC,oBAAoB,IAAI,EAAE,CAAC;iBACrC;gBACD,6BAA6B,EAAE,MAAM,CAAC,WAAW;gBACjD,kBAAkB,EAAE,gBAAgB;gBACpC,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;oBAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;oBACnD,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC1E,CAAC;aACF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE3C,uEAAuE;QACvE,kEAAkE;QAClE,MAAM,cAAc,GAClB,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC;QAC3D,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CACvE,qBAAqB,CACtB,CAAC;YACF,IAAI,GAAG,cAAc;gBACnB,CAAC,CAAC,4BAA4B,CAAC;oBAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,GAAG;oBACH,GAAG;oBACH,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9D,WAAW,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW;oBAC7C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC1B,CAAC;gBACJ,CAAC,CAAC,kBAAkB,EAAE,CAAC;QAC3B,CAAC;QACD,MAAM,KAAK,GAAG,cAAc;YAC1B,CAAC,CAAC,MAAM,CAAC,KAAK;YACd,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,MAAM,WAAW,GAAG;YAClB,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;YAC3B,GAAG,CAAC,cAAc;gBAChB,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;gBAClE,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,8BAA8B,CAAC;YACnD,QAAQ;YACR,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC;YACnE,KAAK;YACL,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa;gBAC5C,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAE,EAAE;gBAClE,CAAC,CAAC,EAAE,CAAC;YACP,KAAK;YACL,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC1B,oCAAoC,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAEjC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,qBAAqB,GAAG,IAAI;YAC1B,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM;YACzC,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,WAAW,GAAG,OAAO,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC;QACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM;YACN,IAAI,EAAE,QAAQ;YACd,KAAK,CAAC,OAAO;gBACX,IAAI,QAAQ;oBAAE,OAAO;gBACrB,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,OAAO,EAAE,CAAC;YAClB,CAAC;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CreateRecipeSessionOptions, RecipeSessionHandle } from "./session.js";
|
|
2
|
+
export interface RecipeEngineHostAdapter {
|
|
3
|
+
/**
|
|
4
|
+
* Create a session the way the host's runtime does — through its own
|
|
5
|
+
* engine invocation, with the host's default injections applied. The
|
|
6
|
+
* options here layer the suite's fixture on top.
|
|
7
|
+
*/
|
|
8
|
+
createSession(options: CreateRecipeSessionOptions): Promise<RecipeSessionHandle>;
|
|
9
|
+
}
|
|
10
|
+
export interface HostConformanceCase {
|
|
11
|
+
name: string;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
interface FixtureOptions {
|
|
15
|
+
agentExtras?: string[];
|
|
16
|
+
manifestPi?: Record<string, unknown>;
|
|
17
|
+
subagents?: string[];
|
|
18
|
+
}
|
|
19
|
+
/** Write a minimal recipe package into a fresh temp dir. */
|
|
20
|
+
export declare function writeFixtureRecipe(options?: FixtureOptions): {
|
|
21
|
+
recipeDir: string;
|
|
22
|
+
workspaceDir: string;
|
|
23
|
+
cleanup(): void;
|
|
24
|
+
};
|
|
25
|
+
export declare function hostConformanceCases(host: RecipeEngineHostAdapter): HostConformanceCase[];
|
|
26
|
+
/**
|
|
27
|
+
* A process env for fixtures: PATH and platform basics without provider
|
|
28
|
+
* keys, so credential-resolution cases are deterministic.
|
|
29
|
+
*/
|
|
30
|
+
export declare function cleanEnv(): NodeJS.ProcessEnv;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=test-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,0BAA0B,EAC1B,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,aAAa,CACX,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,cAAmB,GAAG;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,IAAI,IAAI,CAAC;CACjB,CAuDA;AA2BD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,uBAAuB,GAC5B,mBAAmB,EAAE,CA+KvB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAAC,UAAU,CAO5C"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host conformance suite for the recipe engine.
|
|
3
|
+
*
|
|
4
|
+
* A host that adopts `createRecipeSession` with its own injected pieces —
|
|
5
|
+
* credential store, synthesized MCP bindings, run controller — runs these
|
|
6
|
+
* cases in its own CI. Passing the suite is what "supported host" means: the
|
|
7
|
+
* engine's contract cannot drift from its consumers silently.
|
|
8
|
+
*
|
|
9
|
+
* The cases are runner-agnostic: each is a `{ name, run }` pair that throws
|
|
10
|
+
* on failure. In vitest:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { hostConformanceCases } from "@introspection-ai/recipes/test-utils";
|
|
14
|
+
* for (const c of hostConformanceCases(myHostAdapter)) it(c.name, c.run);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
|
21
|
+
/** Write a minimal recipe package into a fresh temp dir. */
|
|
22
|
+
export function writeFixtureRecipe(options = {}) {
|
|
23
|
+
const root = mkdtempSync(join(tmpdir(), "recipe-conformance-"));
|
|
24
|
+
const recipeDir = join(root, "recipe");
|
|
25
|
+
const workspaceDir = join(root, "workspace");
|
|
26
|
+
mkdirSync(join(recipeDir, "agents"), { recursive: true });
|
|
27
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
28
|
+
writeFileSync(join(recipeDir, "package.json"), JSON.stringify({
|
|
29
|
+
name: "conformance-fixture",
|
|
30
|
+
version: "0.1.0",
|
|
31
|
+
type: "module",
|
|
32
|
+
pi: { agents: ["agents/*.yaml"], ...(options.manifestPi ?? {}) },
|
|
33
|
+
}));
|
|
34
|
+
writeFileSync(join(recipeDir, "SYSTEM.md"), "You are a conformance fixture.\n");
|
|
35
|
+
writeFileSync(join(recipeDir, "agents", "agent.yaml"), [
|
|
36
|
+
"name: agent",
|
|
37
|
+
"model:",
|
|
38
|
+
" name: anthropic/claude-sonnet-4-5",
|
|
39
|
+
" thinking_level: low",
|
|
40
|
+
"tools: [read]",
|
|
41
|
+
...(options.subagents?.length
|
|
42
|
+
? ["subagents:", ...options.subagents.map((name) => ` - ${name}`)]
|
|
43
|
+
: []),
|
|
44
|
+
"system_instructions:",
|
|
45
|
+
" mode: append",
|
|
46
|
+
" content: Conformance agent",
|
|
47
|
+
...(options.agentExtras ?? []),
|
|
48
|
+
"",
|
|
49
|
+
].join("\n"));
|
|
50
|
+
for (const name of options.subagents ?? []) {
|
|
51
|
+
writeFileSync(join(recipeDir, "agents", `${name}.yaml`), [
|
|
52
|
+
`name: ${name}`,
|
|
53
|
+
"model:",
|
|
54
|
+
" name: anthropic/claude-sonnet-4-5",
|
|
55
|
+
" thinking_level: low",
|
|
56
|
+
"tools: [read]",
|
|
57
|
+
"system_instructions:",
|
|
58
|
+
" mode: append",
|
|
59
|
+
` content: Subagent ${name}`,
|
|
60
|
+
"",
|
|
61
|
+
].join("\n"));
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
recipeDir,
|
|
65
|
+
workspaceDir,
|
|
66
|
+
cleanup: () => rmSync(root, { recursive: true, force: true }),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function assert(condition, message) {
|
|
70
|
+
if (!condition)
|
|
71
|
+
throw new Error(`Conformance failure: ${message}`);
|
|
72
|
+
}
|
|
73
|
+
async function expectRejection(promise, message) {
|
|
74
|
+
try {
|
|
75
|
+
await promise;
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Conformance failure: expected rejection — ${message}`);
|
|
81
|
+
}
|
|
82
|
+
async function testCredentialStore() {
|
|
83
|
+
const store = new InMemoryCredentialStore();
|
|
84
|
+
await store.modify("anthropic", async () => ({
|
|
85
|
+
type: "api_key",
|
|
86
|
+
key: "conformance-test-key",
|
|
87
|
+
}));
|
|
88
|
+
return store;
|
|
89
|
+
}
|
|
90
|
+
export function hostConformanceCases(host) {
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
name: "constructs a session from a resolved recipe with an injected credential store",
|
|
94
|
+
async run() {
|
|
95
|
+
const fixture = writeFixtureRecipe();
|
|
96
|
+
try {
|
|
97
|
+
const handle = await host.createSession({
|
|
98
|
+
recipeDir: fixture.recipeDir,
|
|
99
|
+
cwd: fixture.workspaceDir,
|
|
100
|
+
credentials: await testCredentialStore(),
|
|
101
|
+
env: { ...cleanEnv() },
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
assert(handle.recipe.agentName === "agent", "agentName resolves");
|
|
105
|
+
assert(handle.session.systemPrompt.includes("conformance fixture"), "SYSTEM.md reaches the system prompt");
|
|
106
|
+
assert(handle.runs, "handle exposes a run controller");
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
await handle.dispose();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
fixture.cleanup();
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "fails closed when the model provider has no credential",
|
|
119
|
+
async run() {
|
|
120
|
+
const fixture = writeFixtureRecipe();
|
|
121
|
+
try {
|
|
122
|
+
const err = await expectRejection(host.createSession({
|
|
123
|
+
recipeDir: fixture.recipeDir,
|
|
124
|
+
cwd: fixture.workspaceDir,
|
|
125
|
+
env: { ...cleanEnv() },
|
|
126
|
+
}), "session construction without credentials");
|
|
127
|
+
assert(/ANTHROPIC_API_KEY/.test(err.message), `error names the expected env var (got: ${err.message})`);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
fixture.cleanup();
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "fails closed on an unbound required MCP server",
|
|
136
|
+
async run() {
|
|
137
|
+
const fixture = writeFixtureRecipe({
|
|
138
|
+
manifestPi: {
|
|
139
|
+
mcp: {
|
|
140
|
+
servers: [
|
|
141
|
+
{ id: "linear", required: true, tools: { include: ["*"] } },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
agentExtras: ["mcp:", " linear:", ' include: ["*"]'],
|
|
146
|
+
});
|
|
147
|
+
try {
|
|
148
|
+
const err = await expectRejection(host.createSession({
|
|
149
|
+
recipeDir: fixture.recipeDir,
|
|
150
|
+
cwd: fixture.workspaceDir,
|
|
151
|
+
credentials: await testCredentialStore(),
|
|
152
|
+
env: { ...cleanEnv() },
|
|
153
|
+
}), "session construction with an unbound required MCP server");
|
|
154
|
+
assert(err.message.includes("linear"), `error names the unbound server (got: ${err.message})`);
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
fixture.cleanup();
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "materializes inline MCP bindings without a local config file",
|
|
163
|
+
async run() {
|
|
164
|
+
const fixture = writeFixtureRecipe({
|
|
165
|
+
manifestPi: {
|
|
166
|
+
mcp: {
|
|
167
|
+
servers: [
|
|
168
|
+
{ id: "linear", required: true, tools: { include: ["*"] } },
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
agentExtras: ["mcp:", " linear:", ' include: ["*"]'],
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
const env = { ...cleanEnv() };
|
|
176
|
+
const handle = await host.createSession({
|
|
177
|
+
recipeDir: fixture.recipeDir,
|
|
178
|
+
cwd: fixture.workspaceDir,
|
|
179
|
+
credentials: await testCredentialStore(),
|
|
180
|
+
env,
|
|
181
|
+
mcpBindings: {
|
|
182
|
+
servers: [
|
|
183
|
+
{
|
|
184
|
+
id: "linear",
|
|
185
|
+
transport: "streamable_http",
|
|
186
|
+
url: "http://127.0.0.1:9/mcp",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
await handle.dispose();
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
fixture.cleanup();
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "errors, never wedges, when a subagent profile does not exist",
|
|
200
|
+
async run() {
|
|
201
|
+
const fixture = writeFixtureRecipe({ subagents: ["helper"] });
|
|
202
|
+
try {
|
|
203
|
+
const handle = await host.createSession({
|
|
204
|
+
recipeDir: fixture.recipeDir,
|
|
205
|
+
cwd: fixture.workspaceDir,
|
|
206
|
+
credentials: await testCredentialStore(),
|
|
207
|
+
env: { ...cleanEnv() },
|
|
208
|
+
});
|
|
209
|
+
try {
|
|
210
|
+
const settled = await Promise.race([
|
|
211
|
+
(async () => {
|
|
212
|
+
try {
|
|
213
|
+
const run = await handle.runs.start({
|
|
214
|
+
name: "ghost",
|
|
215
|
+
prompt: "hello",
|
|
216
|
+
});
|
|
217
|
+
if (run.status === "running") {
|
|
218
|
+
return await handle.runs.wait(run.agent_run_id);
|
|
219
|
+
}
|
|
220
|
+
return run;
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
// A controller may reject the start outright; that is a
|
|
224
|
+
// valid non-wedging behavior.
|
|
225
|
+
return {
|
|
226
|
+
status: "failed",
|
|
227
|
+
error: err instanceof Error ? err.message : String(err),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
})(),
|
|
231
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Conformance failure: ghost subagent start wedged (no settlement in 15s)")), 15_000)),
|
|
232
|
+
]);
|
|
233
|
+
assert(settled.status === "failed", `ghost run settles as failed (got: ${settled.status})`);
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
await handle.dispose();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
fixture.cleanup();
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* A process env for fixtures: PATH and platform basics without provider
|
|
248
|
+
* keys, so credential-resolution cases are deterministic.
|
|
249
|
+
*/
|
|
250
|
+
export function cleanEnv() {
|
|
251
|
+
const keep = ["PATH", "HOME", "TMPDIR", "SHELL", "SystemRoot", "TEMP", "TMP"];
|
|
252
|
+
const env = {};
|
|
253
|
+
for (const key of keep) {
|
|
254
|
+
if (process.env[key] !== undefined)
|
|
255
|
+
env[key] = process.env[key];
|
|
256
|
+
}
|
|
257
|
+
return env;
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=test-utils.js.map
|