@hachej/boring-agent 0.1.17 → 0.1.20

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 (39) hide show
  1. package/dist/{sandbox-handle-store-hK76cTjn.d.ts → agentPluginEvents-zyIvVjsA.d.ts} +28 -32
  2. package/dist/{chunk-F3CE5CNW.js → chunk-B5JECXMG.js} +5 -7
  3. package/dist/front/index.d.ts +23 -7
  4. package/dist/front/index.js +764 -421
  5. package/dist/front/styles.css +61 -0
  6. package/dist/{tool-ui-DSmWuqGe.d.ts → harness-DRrTn_5T.d.ts} +43 -24
  7. package/dist/server/index.d.ts +79 -10
  8. package/dist/server/index.js +251 -60
  9. package/dist/shared/index.d.ts +5 -3
  10. package/dist/shared/index.js +3 -1
  11. package/dist/tool-ui-DIFNGwYd.d.ts +20 -0
  12. package/docs/ACCESSIBILITY.md +55 -0
  13. package/docs/API.md +64 -0
  14. package/docs/CSP.md +40 -0
  15. package/docs/ERROR_CODES.md +54 -0
  16. package/docs/KNOWN_LIMITATIONS.md +100 -0
  17. package/docs/MIGRATION.md +50 -0
  18. package/docs/PERFORMANCE.md +68 -0
  19. package/docs/PLUGINS.md +108 -0
  20. package/docs/README.md +17 -0
  21. package/docs/RISKS-MULTI-TAB.md +46 -0
  22. package/docs/STYLING.md +71 -0
  23. package/docs/UI-SHADCN.md +56 -0
  24. package/docs/VERCEL_COSTS.md +103 -0
  25. package/docs/plans/AGENT_EVAL_FRAMEWORK.md +466 -0
  26. package/docs/plans/agent-package-spec.md +1777 -0
  27. package/docs/plans/harness-followup-capabilities.md +440 -0
  28. package/docs/plans/harness-tool-ui-capabilities.md +144 -0
  29. package/docs/plans/pi-followup-history-projection.md +229 -0
  30. package/docs/plans/pi-tools-migration.md +1061 -0
  31. package/docs/plans/reviews/pi-followup-history-codex-review.md +11 -0
  32. package/docs/plans/reviews/pi-followup-history-gpt-review.md +64 -0
  33. package/docs/plans/reviews/pi-followup-history-opus-review.md +43 -0
  34. package/docs/plans/reviews/pi-followup-history-xai-review.md +43 -0
  35. package/docs/plans/vercel-base-snapshot-template-plan.md +527 -0
  36. package/docs/plans/vercel-persistent-sandbox-adapter.md +553 -0
  37. package/docs/runtime.md +56 -0
  38. package/docs/tools.md +75 -0
  39. package/package.json +4 -3
@@ -2033,13 +2033,13 @@ function fileRoutes(app, opts, done) {
2033
2033
  // src/server/workspace/provisionRuntime.ts
2034
2034
  import { createHash as createHash3 } from "crypto";
2035
2035
  import { constants as constants2 } from "fs";
2036
- import { access as access2, chmod as chmod3, cp, mkdir as mkdir4, readdir as readdir2, readFile as readFile4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
2037
- import { dirname as dirname5, join as join3, resolve as resolve3 } from "path";
2036
+ import { access as access2, chmod as chmod3, cp, mkdir as mkdir4, readdir as readdir2, readFile as readFile4, realpath as realpath2, stat as stat3, writeFile as writeFile4 } from "fs/promises";
2037
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join3, relative as relative5, resolve as resolve3 } from "path";
2038
2038
  import { fileURLToPath } from "url";
2039
2039
  import { execFile } from "child_process";
2040
2040
  import { promisify } from "util";
2041
2041
  var execFileAsync = promisify(execFile);
2042
- var PROVISIONING_VERSION = 1;
2042
+ var PROVISIONING_VERSION = 2;
2043
2043
  function toPath(value) {
2044
2044
  return value instanceof URL ? fileURLToPath(value) : value;
2045
2045
  }
@@ -2103,6 +2103,27 @@ async function fingerprint(contributions) {
2103
2103
  hash.update(JSON.stringify(Object.fromEntries(Object.entries(spec.env ?? {}).map(([k, v]) => [k, String(v)]))));
2104
2104
  if (await exists(projectFile)) await hashPath(dirname5(projectFile), hash);
2105
2105
  }
2106
+ for (const spec of provisioning.nodePackages ?? []) {
2107
+ const packageRoot = toPath(spec.packageRoot);
2108
+ hash.update(`node-package:${spec.id}:${spec.packageName}:${packageRoot}
2109
+ `);
2110
+ const packageJson = join3(packageRoot, "package.json");
2111
+ const distDir = join3(packageRoot, "dist");
2112
+ const docsDir = join3(packageRoot, "docs");
2113
+ const skillsDir = join3(packageRoot, "skills");
2114
+ const referencesDir = join3(packageRoot, "references");
2115
+ const sourceDocsDir = join3(packageRoot, "src", "server", "docs");
2116
+ if (await exists(packageJson)) await hashPath(packageJson, hash);
2117
+ if (await exists(distDir)) await hashPath(distDir, hash);
2118
+ const templatesDir = join3(packageRoot, "templates");
2119
+ const publicDir = join3(packageRoot, "public");
2120
+ if (await exists(templatesDir)) await hashPath(templatesDir, hash);
2121
+ if (await exists(publicDir)) await hashPath(publicDir, hash);
2122
+ if (await exists(docsDir)) await hashPath(docsDir, hash);
2123
+ if (await exists(skillsDir)) await hashPath(skillsDir, hash);
2124
+ if (await exists(referencesDir)) await hashPath(referencesDir, hash);
2125
+ else if (!await exists(distDir) && await exists(sourceDocsDir)) await hashPath(sourceDocsDir, hash);
2126
+ }
2106
2127
  }
2107
2128
  return `sha256:${hash.digest("hex")}`;
2108
2129
  }
@@ -2117,10 +2138,23 @@ function collectEnv(contributions) {
2117
2138
  }
2118
2139
  return env;
2119
2140
  }
2141
+ function resolveTemplateTarget(workspaceRoot, target) {
2142
+ const rawTarget = target ?? ".";
2143
+ if (rawTarget.length === 0 || rawTarget.includes("\0") || rawTarget.includes("\\") || rawTarget.startsWith("/") || rawTarget.startsWith("//") || /^[A-Za-z]:[\\/]/.test(rawTarget) || rawTarget.split("/").includes("..")) {
2144
+ throw new Error(`Unsafe runtime template target: ${JSON.stringify(rawTarget)}. Template targets must be relative paths inside the workspace.`);
2145
+ }
2146
+ const root = resolve3(workspaceRoot);
2147
+ const resolved = resolve3(root, rawTarget);
2148
+ const rel = relative5(root, resolved);
2149
+ if (rel.startsWith("..") || isAbsolute4(rel)) {
2150
+ throw new Error(`Unsafe runtime template target: ${JSON.stringify(rawTarget)} resolves outside the workspace.`);
2151
+ }
2152
+ return resolved;
2153
+ }
2120
2154
  async function seedTemplates(workspaceRoot, contributions) {
2121
2155
  for (const { provisioning } of contributions) {
2122
2156
  for (const template of provisioning.templateDirs ?? []) {
2123
- await cp(toPath(template.path), resolve3(workspaceRoot, template.target ?? "."), {
2157
+ await cp(toPath(template.path), resolveTemplateTarget(workspaceRoot, template.target), {
2124
2158
  recursive: true,
2125
2159
  force: false,
2126
2160
  errorOnExist: false
@@ -2128,6 +2162,47 @@ async function seedTemplates(workspaceRoot, contributions) {
2128
2162
  }
2129
2163
  }
2130
2164
  }
2165
+ function nodePackageTarget(workspaceRoot, packageName) {
2166
+ const parts = packageName.split("/").filter(Boolean);
2167
+ if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) {
2168
+ throw new Error(`Invalid node package name: ${packageName}`);
2169
+ }
2170
+ return join3(workspaceRoot, "node_modules", ...parts);
2171
+ }
2172
+ async function copyIfExists(source, target) {
2173
+ if (!await exists(source)) return false;
2174
+ if (resolve3(source) === resolve3(target)) return true;
2175
+ if (await exists(target) && await realpath2(source) === await realpath2(target)) return true;
2176
+ await cp(source, target, {
2177
+ recursive: true,
2178
+ force: true,
2179
+ errorOnExist: false
2180
+ });
2181
+ return true;
2182
+ }
2183
+ async function ensureNodePackages(workspaceRoot, specs) {
2184
+ for (const spec of specs) {
2185
+ const packageRoot = toPath(spec.packageRoot);
2186
+ const target = nodePackageTarget(workspaceRoot, spec.packageName);
2187
+ await mkdir4(target, { recursive: true });
2188
+ const copiedPackageJson = await copyIfExists(join3(packageRoot, "package.json"), join3(target, "package.json"));
2189
+ if (!copiedPackageJson) {
2190
+ throw new Error(`Node package provisioning source is missing package.json: ${packageRoot}`);
2191
+ }
2192
+ await copyIfExists(join3(packageRoot, "dist"), join3(target, "dist"));
2193
+ await copyIfExists(join3(packageRoot, "templates"), join3(target, "templates"));
2194
+ await copyIfExists(join3(packageRoot, "public"), join3(target, "public"));
2195
+ if (!await exists(join3(target, "dist", "docs", "plugins.md"))) {
2196
+ await copyIfExists(join3(packageRoot, "src", "server", "docs"), join3(target, "dist", "docs"));
2197
+ }
2198
+ if (!await copyIfExists(join3(packageRoot, "docs"), join3(target, "docs"))) {
2199
+ await copyIfExists(join3(packageRoot, "src", "server", "docs"), join3(target, "docs"));
2200
+ }
2201
+ await copyIfExists(join3(packageRoot, "skills"), join3(target, "skills"));
2202
+ await copyIfExists(join3(packageRoot, "references"), join3(target, "references"));
2203
+ await copyIfExists(join3(packageRoot, "src", "globals.css"), join3(target, "src", "globals.css"));
2204
+ }
2205
+ }
2131
2206
  async function ensurePython(workspaceRoot, specs) {
2132
2207
  if (specs.length === 0) return;
2133
2208
  const venvPython = join3(workspaceRoot, ".venv", "bin", "python");
@@ -2164,6 +2239,11 @@ function assertEnvKey(key) {
2164
2239
  async function isRuntimeMaterialized(workspaceRoot, contributions) {
2165
2240
  const hasPython = contributions.some(({ provisioning }) => (provisioning.python ?? []).length > 0);
2166
2241
  if (hasPython && !await exists(join3(workspaceRoot, ".venv", "bin", "python"))) return false;
2242
+ for (const { provisioning } of contributions) {
2243
+ for (const spec of provisioning.nodePackages ?? []) {
2244
+ if (!await exists(join3(nodePackageTarget(workspaceRoot, spec.packageName), "package.json"))) return false;
2245
+ }
2246
+ }
2167
2247
  return true;
2168
2248
  }
2169
2249
  async function writeShims(workspaceRoot, env) {
@@ -2232,6 +2312,7 @@ async function provisionRuntimeWorkspace({
2232
2312
  }
2233
2313
  }
2234
2314
  await seedTemplates(workspaceRoot, active);
2315
+ await ensureNodePackages(workspaceRoot, active.flatMap(({ provisioning }) => provisioning.nodePackages ?? []));
2235
2316
  await ensurePython(workspaceRoot, active.flatMap(({ provisioning }) => provisioning.python ?? []));
2236
2317
  const actualBinDir = await writeShims(workspaceRoot, env);
2237
2318
  await mkdir4(dirname5(markerPath), { recursive: true });
@@ -2649,8 +2730,10 @@ function createServerFileSearch(workspace, sandbox) {
2649
2730
  const command = [
2650
2731
  "find .",
2651
2732
  "-maxdepth 10",
2733
+ "-path './.boring-agent' -prune -o",
2652
2734
  buildFindArgs(glob),
2653
2735
  "-type f",
2736
+ "-print",
2654
2737
  `| head -n ${safeLimit}`
2655
2738
  ].join(" ");
2656
2739
  const { stdout, exitCode } = await sandbox.exec(command, {
@@ -3274,7 +3357,6 @@ function resolveMode(mode = autoDetectMode()) {
3274
3357
 
3275
3358
  // src/server/createAgentApp.ts
3276
3359
  import Fastify from "fastify";
3277
- import { basename as basename2 } from "path";
3278
3360
 
3279
3361
  // src/server/harness/pi-coding-agent/createHarness.ts
3280
3362
  import { existsSync, readFileSync as readFileSync2 } from "fs";
@@ -3287,7 +3369,8 @@ import {
3287
3369
  ModelRegistry,
3288
3370
  DefaultResourceLoader,
3289
3371
  SettingsManager as SettingsManager2,
3290
- getAgentDir as getAgentDir2
3372
+ getAgentDir as getAgentDir2,
3373
+ loadSkills
3291
3374
  } from "@mariozechner/pi-coding-agent";
3292
3375
 
3293
3376
  // src/server/harness/pi-coding-agent/tool-adapter.ts
@@ -4216,6 +4299,17 @@ var WORKSPACE_PATHS_GUIDELINE = [
4216
4299
  function composeSystemPromptAppend(hostAppend) {
4217
4300
  return [WORKSPACE_PATHS_GUIDELINE, hostAppend?.trim()].filter(Boolean).join("\n\n");
4218
4301
  }
4302
+ function buildDynamicPromptExtension(source) {
4303
+ return (pi) => {
4304
+ pi.on("before_agent_start", async (event) => {
4305
+ const extra = (await source())?.trim();
4306
+ if (!extra) return;
4307
+ return { systemPrompt: `${event.systemPrompt}
4308
+
4309
+ ${extra}` };
4310
+ });
4311
+ };
4312
+ }
4219
4313
  function extractUserMessageText(message) {
4220
4314
  const record = message;
4221
4315
  if (record?.role !== "user") return "";
@@ -4341,6 +4435,30 @@ function createPiCodingAgentHarness(opts) {
4341
4435
  sessionDir: opts.sessionDir
4342
4436
  });
4343
4437
  const piSessions = /* @__PURE__ */ new Map();
4438
+ const effectiveSkillPaths = [];
4439
+ const effectivePackages = [];
4440
+ const effectiveExtensionPaths = [];
4441
+ const refreshEffectiveResources = () => {
4442
+ const dynamic = opts.pi?.getHotReloadableResources?.() ?? {};
4443
+ effectiveSkillPaths.splice(
4444
+ 0,
4445
+ effectiveSkillPaths.length,
4446
+ ...opts.pi?.additionalSkillPaths ?? [],
4447
+ ...dynamic.additionalSkillPaths ?? []
4448
+ );
4449
+ effectivePackages.splice(
4450
+ 0,
4451
+ effectivePackages.length,
4452
+ ...mergePiPackageSources(opts.pi?.packages ?? [], dynamic.packages ?? [])
4453
+ );
4454
+ effectiveExtensionPaths.splice(
4455
+ 0,
4456
+ effectiveExtensionPaths.length,
4457
+ ...opts.pi?.extensionPaths ?? [],
4458
+ ...dynamic.extensionPaths ?? []
4459
+ );
4460
+ };
4461
+ refreshEffectiveResources();
4344
4462
  const scheduleSessionTitle = createSessionTitleScheduler({
4345
4463
  loadSession: (sessionId) => sessionStore.load({ workspaceId: "default" }, sessionId),
4346
4464
  writeTitle: (sessionId, title) => {
@@ -4373,23 +4491,44 @@ function createPiCodingAgentHarness(opts) {
4373
4491
  }
4374
4492
  const resolvedModel = resolveRequestedModel(modelRegistry, input);
4375
4493
  const model = resolvedModel ?? resolveDefaultModel(modelRegistry);
4494
+ refreshEffectiveResources();
4376
4495
  const composedSystemPromptAppend = composeSystemPromptAppend(opts.systemPromptAppend);
4496
+ const dynamicPromptExtension = opts.systemPromptDynamic ? buildDynamicPromptExtension(opts.systemPromptDynamic) : void 0;
4377
4497
  const agentDir = getAgentDir2();
4378
- const additionalSkillPaths = opts.resourceLoaderOptions?.additionalSkillPaths ?? [];
4379
- const piPackages = opts.resourceLoaderOptions?.piPackages ?? [];
4498
+ const extensionFactories = [
4499
+ ...dynamicPromptExtension ? [dynamicPromptExtension] : [],
4500
+ ...opts.pi?.extensionFactories ?? []
4501
+ ];
4380
4502
  const settingsManager = createResourceSettingsManager(
4381
4503
  ctx.workdir,
4382
4504
  agentDir,
4383
- piPackages
4505
+ effectivePackages
4384
4506
  );
4385
4507
  const resourceLoader = new DefaultResourceLoader({
4386
4508
  cwd: ctx.workdir,
4387
4509
  agentDir,
4388
4510
  settingsManager,
4389
4511
  appendSystemPromptOverride: (base) => [...base, composedSystemPromptAppend],
4390
- ...opts.resourceLoaderOptions?.noContextFiles ? { noContextFiles: true } : {},
4391
- ...opts.resourceLoaderOptions?.noSkills ? { noSkills: true } : {},
4392
- ...additionalSkillPaths.length ? { additionalSkillPaths } : {}
4512
+ ...effectiveExtensionPaths.length ? { additionalExtensionPaths: effectiveExtensionPaths } : {},
4513
+ ...extensionFactories.length ? { extensionFactories } : {},
4514
+ ...opts.pi?.noContextFiles ? { noContextFiles: true } : {},
4515
+ ...opts.pi?.noSkills ? { noSkills: true } : {},
4516
+ ...effectiveSkillPaths.length ? { additionalSkillPaths: effectiveSkillPaths } : {},
4517
+ // skillsOverride REPLACES Pi's resolved skill set, which includes
4518
+ // skills contributed by host-declared pi packages (e.g.
4519
+ // @hachej/boring-pi → boring-plugin-authoring). Only trigger it for
4520
+ // the explicit `noSkills` opt-out, where the host wants a clean slate.
4521
+ // Passing additionalSkillPaths is not, by itself, a request to throw
4522
+ // away package skills — those should keep flowing through Pi's loader
4523
+ // and merge with the additional paths.
4524
+ ...opts.pi?.noSkills ? {
4525
+ skillsOverride: () => loadSkills({
4526
+ cwd: ctx.workdir,
4527
+ agentDir,
4528
+ skillPaths: effectiveSkillPaths,
4529
+ includeDefaults: false
4530
+ })
4531
+ } : {}
4393
4532
  });
4394
4533
  await resourceLoader?.reload();
4395
4534
  const { session: piSession } = await createAgentSession({
@@ -4414,6 +4553,13 @@ function createPiCodingAgentHarness(opts) {
4414
4553
  piSessions.set(sessionId, handle);
4415
4554
  return handle;
4416
4555
  }
4556
+ async function reloadPiSession(sessionId) {
4557
+ const handle = piSessions.get(sessionId);
4558
+ if (!handle) return false;
4559
+ refreshEffectiveResources();
4560
+ await handle.piSession.reload();
4561
+ return true;
4562
+ }
4417
4563
  function disposePiSession(sessionId) {
4418
4564
  const handle = piSessions.get(sessionId);
4419
4565
  if (!handle) return;
@@ -4526,6 +4672,7 @@ function createPiCodingAgentHarness(opts) {
4526
4672
  getSystemPrompt(sessionId) {
4527
4673
  return piSessions.get(sessionId)?.piSession.systemPrompt;
4528
4674
  },
4675
+ reloadSession: reloadPiSession,
4529
4676
  async *sendMessage(input, ctx) {
4530
4677
  const { piSession } = await getOrCreatePiSession(
4531
4678
  input.sessionId,
@@ -4926,7 +5073,7 @@ ${attachmentNotes.join("\n")}` : message,
4926
5073
 
4927
5074
  // src/server/harness/pi-coding-agent/pluginLoader.ts
4928
5075
  import { readdir as readdir5, stat as stat5, readFile as readFile7 } from "fs/promises";
4929
- import { join as join7, extname as extname3, resolve as resolve4, sep as sep3, relative as relative5 } from "path";
5076
+ import { join as join7, extname as extname3, resolve as resolve4, sep as sep3, relative as relative6 } from "path";
4930
5077
  import { homedir as homedir4 } from "os";
4931
5078
  import { pathToFileURL } from "url";
4932
5079
  var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs"]);
@@ -5001,7 +5148,7 @@ async function loadNpmPlugin(pkgDir, importFn) {
5001
5148
  if (resolvedMain !== resolvedPkgDir && !resolvedMain.startsWith(resolvedPkgDir + sep3)) {
5002
5149
  return [];
5003
5150
  }
5004
- const relPath = relative5(resolvedPkgDir, resolvedMain);
5151
+ const relPath = relative6(resolvedPkgDir, resolvedMain);
5005
5152
  if (relPath.startsWith("..")) {
5006
5153
  return [];
5007
5154
  }
@@ -5085,8 +5232,8 @@ import {
5085
5232
 
5086
5233
  // src/server/tools/operations/bound.ts
5087
5234
  import { constants as constants3 } from "fs";
5088
- import { access as access3, lstat as lstat3, mkdir as mkdir10, readFile as readFile8, readdir as readdir6, readlink, realpath as realpath2, stat as stat6, writeFile as writeFile8 } from "fs/promises";
5089
- import { dirname as dirname7, isAbsolute as isAbsolute4, relative as relative6, resolve as resolve5 } from "path";
5235
+ import { access as access3, lstat as lstat3, mkdir as mkdir10, readFile as readFile8, readdir as readdir6, readlink, realpath as realpath3, stat as stat6, writeFile as writeFile8 } from "fs/promises";
5236
+ import { dirname as dirname7, isAbsolute as isAbsolute5, relative as relative7, resolve as resolve5 } from "path";
5090
5237
  function toPosixPath(value) {
5091
5238
  return value.split("\\").join("/");
5092
5239
  }
@@ -5131,8 +5278,8 @@ function matchesGlob(relativePath, pattern) {
5131
5278
  }
5132
5279
  function shouldSkipDir(relativePath, ignore) {
5133
5280
  const normalizedRel = toPosixPath(relativePath);
5134
- const basename4 = normalizedRel.split("/").at(-1) ?? normalizedRel;
5135
- if (basename4 === ".git" || basename4 === "node_modules") return true;
5281
+ const basename3 = normalizedRel.split("/").at(-1) ?? normalizedRel;
5282
+ if (basename3 === ".git" || basename3 === "node_modules") return true;
5136
5283
  return ignore.some((pattern) => {
5137
5284
  return matchesGlob(normalizedRel, pattern) || matchesGlob(`${normalizedRel}/`, pattern);
5138
5285
  });
@@ -5143,7 +5290,7 @@ async function walkMatches(root, current, pattern, ignore, limit, out) {
5143
5290
  for (const entry of entries) {
5144
5291
  if (out.length >= limit) return;
5145
5292
  const absolutePath = resolve5(current, entry.name);
5146
- const relativePath = toPosixPath(relative6(root, absolutePath));
5293
+ const relativePath = toPosixPath(relative7(root, absolutePath));
5147
5294
  if (entry.isDirectory() && shouldSkipDir(relativePath, ignore)) continue;
5148
5295
  if (matchesGlob(relativePath, pattern)) {
5149
5296
  out.push(absolutePath);
@@ -5167,16 +5314,16 @@ async function findNearestExistingAncestor(absPath) {
5167
5314
  }
5168
5315
  }
5169
5316
  async function assertWithinWorkspace(workspaceRoot, absPath) {
5170
- const realRoot = await realpath2(resolve5(workspaceRoot));
5317
+ const realRoot = await realpath3(resolve5(workspaceRoot));
5171
5318
  try {
5172
5319
  const s = await lstat3(absPath);
5173
5320
  if (s.isSymbolicLink()) {
5174
5321
  const target = await readlink(absPath);
5175
5322
  const resolvedTarget = resolve5(dirname7(absPath), target);
5176
5323
  const nearestAncestor = await findNearestExistingAncestor(resolvedTarget);
5177
- const realAncestor = await realpath2(nearestAncestor);
5178
- const rel2 = relative6(realRoot, realAncestor);
5179
- if (rel2.startsWith("..") || isAbsolute4(rel2)) {
5324
+ const realAncestor = await realpath3(nearestAncestor);
5325
+ const rel2 = relative7(realRoot, realAncestor);
5326
+ if (rel2.startsWith("..") || isAbsolute5(rel2)) {
5180
5327
  throw new Error(`path "${absPath}" is outside workspace`);
5181
5328
  }
5182
5329
  return;
@@ -5190,22 +5337,22 @@ async function assertWithinWorkspace(workspaceRoot, absPath) {
5190
5337
  }
5191
5338
  let realCandidate;
5192
5339
  try {
5193
- realCandidate = await realpath2(absPath);
5340
+ realCandidate = await realpath3(absPath);
5194
5341
  } catch (err) {
5195
5342
  const code = err.code;
5196
5343
  if (code === "ENOENT") {
5197
5344
  const nearestAncestor = await findNearestExistingAncestor(dirname7(absPath));
5198
- const realAncestor = await realpath2(nearestAncestor);
5199
- const rel2 = relative6(realRoot, realAncestor);
5200
- if (rel2.startsWith("..") || isAbsolute4(rel2)) {
5345
+ const realAncestor = await realpath3(nearestAncestor);
5346
+ const rel2 = relative7(realRoot, realAncestor);
5347
+ if (rel2.startsWith("..") || isAbsolute5(rel2)) {
5201
5348
  throw new Error(`path "${absPath}" is outside workspace`);
5202
5349
  }
5203
5350
  return;
5204
5351
  }
5205
5352
  throw err;
5206
5353
  }
5207
- const rel = relative6(realRoot, realCandidate);
5208
- if (rel.startsWith("..") || isAbsolute4(rel)) {
5354
+ const rel = relative7(realRoot, realCandidate);
5355
+ if (rel.startsWith("..") || isAbsolute5(rel)) {
5209
5356
  throw new Error(`path "${absPath}" is outside workspace`);
5210
5357
  }
5211
5358
  }
@@ -5296,7 +5443,7 @@ function boundFs(workspaceRoot) {
5296
5443
  }
5297
5444
 
5298
5445
  // src/server/tools/operations/vercel.ts
5299
- import { isAbsolute as isAbsolute5, relative as relative7 } from "path";
5446
+ import { isAbsolute as isAbsolute6, relative as relative8 } from "path";
5300
5447
  function rootAliases(workspace) {
5301
5448
  const aliases = [workspace.root];
5302
5449
  if (workspace.root === VERCEL_SANDBOX_WORKSPACE_ROOT) aliases.push(VERCEL_SANDBOX_REMOTE_ROOT);
@@ -5304,11 +5451,11 @@ function rootAliases(workspace) {
5304
5451
  return aliases;
5305
5452
  }
5306
5453
  function isOutsideWorkspaceRel(rel) {
5307
- return rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute5(rel);
5454
+ return rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute6(rel);
5308
5455
  }
5309
5456
  function toRelPath(workspace, absolutePath) {
5310
5457
  for (const root of rootAliases(workspace)) {
5311
- const rel = relative7(root, absolutePath);
5458
+ const rel = relative8(root, absolutePath);
5312
5459
  if (!isOutsideWorkspaceRel(rel)) return rel;
5313
5460
  }
5314
5461
  const skillMarker = "/.agents/skills/";
@@ -5491,7 +5638,7 @@ import {
5491
5638
  truncateHead,
5492
5639
  truncateLine
5493
5640
  } from "@mariozechner/pi-coding-agent";
5494
- import { resolve as resolve6, relative as relative8 } from "path";
5641
+ import { resolve as resolve6, relative as relative9 } from "path";
5495
5642
 
5496
5643
  // src/server/catalog/tools/_shared.ts
5497
5644
  function makeError(message) {
@@ -5644,7 +5791,7 @@ function vercelGrepTool(sandbox, workspaceRoot) {
5644
5791
  }
5645
5792
  if (workspaceRoot && typeof params.path === "string" && params.path.length > 0) {
5646
5793
  const resolved = resolve6(workspaceRoot, params.path);
5647
- const rel = relative8(workspaceRoot, resolved);
5794
+ const rel = relative9(workspaceRoot, resolved);
5648
5795
  if (rel.startsWith("..") || rel.startsWith("/")) {
5649
5796
  return makeError(`path "${params.path}" is outside workspace`);
5650
5797
  }
@@ -5804,8 +5951,7 @@ function createExecuteIsolatedCodeTool(sandbox) {
5804
5951
  language: { type: "string", enum: ["python", "shell"] },
5805
5952
  image: { type: "string" },
5806
5953
  packages: { type: "array", items: { type: "string" } },
5807
- sandboxId: { type: "string" },
5808
- vmSize: { type: "string", enum: ["xxs", "xs", "s", "m", "l"] }
5954
+ sandboxId: { type: "string" }
5809
5955
  },
5810
5956
  required: ["code", "language"],
5811
5957
  additionalProperties: false
@@ -5831,8 +5977,7 @@ function createExecuteIsolatedCodeTool(sandbox) {
5831
5977
  language,
5832
5978
  image: typeof input.image === "string" ? input.image : void 0,
5833
5979
  packages: Array.isArray(input.packages) ? input.packages.filter((v) => typeof v === "string") : void 0,
5834
- sandboxId: typeof input.sandboxId === "string" ? input.sandboxId : void 0,
5835
- vmSize: input.vmSize === "xxs" || input.vmSize === "xs" || input.vmSize === "s" || input.vmSize === "m" || input.vmSize === "l" ? input.vmSize : void 0
5980
+ sandboxId: typeof input.sandboxId === "string" ? input.sandboxId : void 0
5836
5981
  });
5837
5982
  return {
5838
5983
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -6751,7 +6896,7 @@ function modelsRoutes(app, _opts, done) {
6751
6896
  import {
6752
6897
  DefaultPackageManager,
6753
6898
  getAgentDir as getAgentDir3,
6754
- loadSkills
6899
+ loadSkills as loadSkills2
6755
6900
  } from "@mariozechner/pi-coding-agent";
6756
6901
  var CACHE_TTL_MS2 = 3e4;
6757
6902
  function skillsRoutes(app, opts, done) {
@@ -6786,7 +6931,7 @@ function skillsRoutes(app, opts, done) {
6786
6931
  const resolved = await packageManager.resolve();
6787
6932
  return resolved.skills.filter((resource) => resource.enabled).map((resource) => resource.path);
6788
6933
  })();
6789
- const result = loadSkills({
6934
+ const result = loadSkills2({
6790
6935
  cwd: workspaceRoot,
6791
6936
  agentDir,
6792
6937
  skillPaths: [...packageSkillPaths, ...additionalSkillPaths ?? []],
@@ -7329,6 +7474,33 @@ data: ${JSON.stringify(event)}
7329
7474
  done();
7330
7475
  }
7331
7476
 
7477
+ // src/server/http/routes/reload.ts
7478
+ function reloadRoutes(app, opts, done) {
7479
+ app.post("/api/v1/agent/reload", async (request, reply) => {
7480
+ if (!opts.harness.reloadSession) {
7481
+ return reply.status(501).send({ ok: false, error: "Agent harness does not support reload" });
7482
+ }
7483
+ const sessionId = request.body?.sessionId || opts.defaultSessionId;
7484
+ try {
7485
+ const hookResult = await opts.beforeReload?.();
7486
+ const reloaded = await opts.harness.reloadSession(sessionId);
7487
+ const restart_warnings = hookResult?.restart_warnings;
7488
+ const diagnostics = hookResult?.diagnostics;
7489
+ return {
7490
+ ok: true,
7491
+ sessionId,
7492
+ reloaded,
7493
+ ...restart_warnings && restart_warnings.length > 0 ? { restart_warnings } : {},
7494
+ ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
7495
+ };
7496
+ } catch (error) {
7497
+ const message = error instanceof Error ? error.message : String(error);
7498
+ return reply.status(422).send({ ok: false, error: message });
7499
+ }
7500
+ });
7501
+ done();
7502
+ }
7503
+
7332
7504
  // src/server/http/routes/search.ts
7333
7505
  var MAX_GLOB_LENGTH = 256;
7334
7506
  var DEFAULT_LIMIT3 = 500;
@@ -7474,13 +7646,21 @@ async function createAgentApp(opts = {}) {
7474
7646
  ...opts.extraTools ?? [],
7475
7647
  ...pluginTools
7476
7648
  ];
7477
- const harness = createPiCodingAgentHarness({
7649
+ const harnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
7650
+ ...input,
7651
+ pi: {
7652
+ noContextFiles: true,
7653
+ noSkills: true,
7654
+ ...opts.pi
7655
+ }
7656
+ }));
7657
+ const harness = await harnessFactory({
7478
7658
  tools,
7479
7659
  cwd: workspaceRoot,
7480
7660
  sessionNamespace: opts.sessionNamespace,
7481
7661
  sessionDir: opts.sessionDir,
7482
7662
  systemPromptAppend: opts.systemPromptAppend,
7483
- resourceLoaderOptions: opts.resourceLoaderOptions
7663
+ systemPromptDynamic: opts.systemPromptDynamic
7484
7664
  });
7485
7665
  const sessionChangesTracker = new InMemorySessionChangesTracker();
7486
7666
  const readyTracker = new ReadyStatusTracker({
@@ -7519,10 +7699,13 @@ async function createAgentApp(opts = {}) {
7519
7699
  await app.register(modelsRoutes);
7520
7700
  await app.register(skillsRoutes, {
7521
7701
  workspaceRoot,
7522
- additionalSkillPaths: opts.resourceLoaderOptions?.additionalSkillPaths
7702
+ additionalSkillPaths: opts.pi?.additionalSkillPaths,
7703
+ piPackages: opts.pi?.packages,
7704
+ noSkills: opts.pi?.noSkills
7523
7705
  });
7524
7706
  await app.register(sessionChangesRoutes, { tracker: sessionChangesTracker });
7525
7707
  await app.register(catalogRoutes, { tools });
7708
+ await app.register(reloadRoutes, { harness, defaultSessionId: sessionId, beforeReload: opts.beforeReload });
7526
7709
  await app.register(readyStatusRoutes, { tracker: readyTracker });
7527
7710
  return app;
7528
7711
  }
@@ -7552,7 +7735,7 @@ function applyCspHeaders(response, opts = {}) {
7552
7735
  }
7553
7736
 
7554
7737
  // src/server/registerAgentRoutes.ts
7555
- import { basename as basename3 } from "path";
7738
+ import { basename as basename2 } from "path";
7556
7739
  import { AuthStorage as AuthStorage3, ModelRegistry as ModelRegistry3 } from "@mariozechner/pi-coding-agent";
7557
7740
 
7558
7741
  // src/server/catalog/mergeTools.ts
@@ -7699,7 +7882,7 @@ var DEFAULT_WORKSPACE_ID3 = "default";
7699
7882
  var STANDARD_AGENT_TOOL_NAMES = ["bash", "read", "write", "edit", "find", "grep", "ls"];
7700
7883
  var VERCEL_BINDING_HEALTHCHECK_INTERVAL_MS = 15e3;
7701
7884
  function pluginNameFromPath(path4) {
7702
- const fileName = basename3(path4);
7885
+ const fileName = basename2(path4);
7703
7886
  if (fileName.endsWith(".mjs")) return fileName.slice(0, -4);
7704
7887
  if (fileName.endsWith(".js")) return fileName.slice(0, -3);
7705
7888
  return fileName;
@@ -7758,7 +7941,7 @@ var registerAgentRoutes = async (app, opts) => {
7758
7941
  app.addHook("onClose", async () => {
7759
7942
  await modeAdapter.dispose?.();
7760
7943
  });
7761
- const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getResourceLoaderOptions === "function" || typeof opts.getSessionNamespace === "function";
7944
+ const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getPi === "function" || typeof opts.getSessionNamespace === "function";
7762
7945
  const sessionChangesTracker = new InMemorySessionChangesTracker();
7763
7946
  const runtimeBindings = /* @__PURE__ */ new Map();
7764
7947
  const MAX_RUNTIME_BINDINGS = 256;
@@ -7772,27 +7955,27 @@ var registerAgentRoutes = async (app, opts) => {
7772
7955
  async function resolveRuntimeScope(workspaceId, request) {
7773
7956
  const root = request && opts.getWorkspaceRoot ? await opts.getWorkspaceRoot(workspaceId, request) : workspaceRoot;
7774
7957
  const scopedTemplatePath = opts.getTemplatePath ? await opts.getTemplatePath({ workspaceId, workspaceRoot: root, request }) : templatePath;
7775
- const resourceLoaderOptions = opts.getResourceLoaderOptions ? await opts.getResourceLoaderOptions({ workspaceId, workspaceRoot: root, request }) : opts.resourceLoaderOptions;
7958
+ const pi = opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot: root, request }) : opts.pi;
7776
7959
  const sessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot: root, request }) : opts.sessionNamespace);
7777
7960
  return {
7778
7961
  root,
7779
7962
  templatePath: scopedTemplatePath,
7780
- resourceLoaderOptions,
7963
+ pi,
7781
7964
  sessionNamespace,
7782
7965
  key: JSON.stringify([
7783
7966
  resolvedMode,
7784
7967
  workspaceId,
7785
7968
  root,
7786
7969
  scopedTemplatePath ?? null,
7787
- resourceLoaderOptions ?? null,
7970
+ pi ?? null,
7788
7971
  sessionNamespace ?? null
7789
7972
  ])
7790
7973
  };
7791
7974
  }
7792
7975
  async function resolveSkillScope(workspaceId, request) {
7793
7976
  const root = request && opts.getWorkspaceRoot ? await opts.getWorkspaceRoot(workspaceId, request) : workspaceRoot;
7794
- const resourceLoaderOptions = opts.getResourceLoaderOptions ? await opts.getResourceLoaderOptions({ workspaceId, workspaceRoot: root, request }) : opts.resourceLoaderOptions;
7795
- return { root, resourceLoaderOptions };
7977
+ const pi = opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot: root, request }) : opts.pi;
7978
+ return { root, pi };
7796
7979
  }
7797
7980
  async function createRuntimeBinding(workspaceId, scope, request) {
7798
7981
  const root = scope.root;
@@ -7837,12 +8020,19 @@ var registerAgentRoutes = async (app, opts) => {
7837
8020
  pluginTools,
7838
8021
  logger: app.log
7839
8022
  });
7840
- const harness = createPiCodingAgentHarness({
8023
+ const harnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
8024
+ ...input,
8025
+ pi: {
8026
+ noContextFiles: true,
8027
+ noSkills: true,
8028
+ ...scope.pi
8029
+ }
8030
+ }));
8031
+ const harness = await harnessFactory({
7841
8032
  tools,
7842
8033
  cwd: root,
7843
8034
  sessionNamespace: scope.sessionNamespace,
7844
- systemPromptAppend: opts.systemPromptAppend,
7845
- resourceLoaderOptions: scope.resourceLoaderOptions
8035
+ systemPromptAppend: opts.systemPromptAppend
7846
8036
  });
7847
8037
  const readyTracker = new ReadyStatusTracker({
7848
8038
  sandboxReady: resolvedMode !== "vercel-sandbox",
@@ -8025,13 +8215,13 @@ var registerAgentRoutes = async (app, opts) => {
8025
8215
  await app.register(modelsRoutes);
8026
8216
  await app.register(skillsRoutes, {
8027
8217
  workspaceRoot,
8028
- additionalSkillPaths: opts.resourceLoaderOptions?.additionalSkillPaths,
8029
- piPackages: opts.resourceLoaderOptions?.piPackages,
8030
- noSkills: opts.resourceLoaderOptions?.noSkills,
8218
+ additionalSkillPaths: opts.pi?.additionalSkillPaths,
8219
+ piPackages: opts.pi?.packages,
8220
+ noSkills: opts.pi?.noSkills,
8031
8221
  getWorkspaceRoot: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).root,
8032
- getAdditionalSkillPaths: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).resourceLoaderOptions?.additionalSkillPaths,
8033
- getPiPackages: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).resourceLoaderOptions?.piPackages,
8034
- getNoSkills: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).resourceLoaderOptions?.noSkills
8222
+ getAdditionalSkillPaths: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).pi?.additionalSkillPaths,
8223
+ getPiPackages: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).pi?.packages,
8224
+ getNoSkills: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).pi?.noSkills
8035
8225
  });
8036
8226
  await app.register(sessionChangesRoutes, { tracker: sessionChangesTracker });
8037
8227
  await app.register(
@@ -8062,6 +8252,7 @@ export {
8062
8252
  createDirectSandbox,
8063
8253
  createLogger,
8064
8254
  createNodeWorkspace,
8255
+ createResourceSettingsManager,
8065
8256
  createVercelDeploymentSnapshotProvider,
8066
8257
  createVercelSandboxWorkspace,
8067
8258
  fileRoutes,
@@ -1,6 +1,8 @@
1
- export { A as AgentHarness, R as RunContext, S as SendMessageInput, a as SessionCtx, b as SessionDetail, c as SessionStore, d as SessionSummary, T as ToolUiMetadata, e as extractToolUiMetadata, i as isToolUiMetadata } from '../tool-ui-DSmWuqGe.js';
2
- import { W as Workspace, S as Sandbox, F as FileSearch, A as AgentTool } from '../sandbox-handle-store-hK76cTjn.js';
3
- export { E as Entry, a as ExecOptions, b as ExecResult, I as IsolatedCodeInput, c as IsolatedCodeOutput, J as JSONSchema, d as SandboxCapability, e as SandboxHandleRecord, f as SandboxHandleStore, g as Stat, T as ToolExecContext, h as ToolResult } from '../sandbox-handle-store-hK76cTjn.js';
1
+ import { A as AgentTool } from '../harness-DRrTn_5T.js';
2
+ export { a as AgentHarness, J as JSONSchema, R as RunContext, S as SendMessageInput, b as SessionCtx, c as SessionDetail, d as SessionStore, e as SessionSummary, T as ToolExecContext, f as ToolResult } from '../harness-DRrTn_5T.js';
3
+ import { W as Workspace, S as Sandbox, F as FileSearch } from '../agentPluginEvents-zyIvVjsA.js';
4
+ export { E as Entry, a as ExecOptions, b as ExecResult, I as IsolatedCodeInput, c as IsolatedCodeOutput, d as SandboxCapability, e as SandboxHandleRecord, f as SandboxHandleStore, g as Stat, h as WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT } from '../agentPluginEvents-zyIvVjsA.js';
5
+ export { T as ToolUiMetadata, e as extractToolUiMetadata, i as isToolUiMetadata } from '../tool-ui-DIFNGwYd.js';
4
6
  export { UIMessage, UIMessageChunk } from 'ai';
5
7
  import { z } from 'zod';
6
8