@hachej/boring-agent 0.1.17 → 0.1.18

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 +243 -54
  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
@@ -2034,12 +2034,12 @@ function fileRoutes(app, opts, done) {
2034
2034
  import { createHash as createHash3 } from "crypto";
2035
2035
  import { constants as constants2 } from "fs";
2036
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";
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,45 @@ 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
+ await cp(source, target, {
2175
+ recursive: true,
2176
+ force: true,
2177
+ errorOnExist: false
2178
+ });
2179
+ return true;
2180
+ }
2181
+ async function ensureNodePackages(workspaceRoot, specs) {
2182
+ for (const spec of specs) {
2183
+ const packageRoot = toPath(spec.packageRoot);
2184
+ const target = nodePackageTarget(workspaceRoot, spec.packageName);
2185
+ await mkdir4(target, { recursive: true });
2186
+ const copiedPackageJson = await copyIfExists(join3(packageRoot, "package.json"), join3(target, "package.json"));
2187
+ if (!copiedPackageJson) {
2188
+ throw new Error(`Node package provisioning source is missing package.json: ${packageRoot}`);
2189
+ }
2190
+ await copyIfExists(join3(packageRoot, "dist"), join3(target, "dist"));
2191
+ await copyIfExists(join3(packageRoot, "templates"), join3(target, "templates"));
2192
+ await copyIfExists(join3(packageRoot, "public"), join3(target, "public"));
2193
+ if (!await exists(join3(target, "dist", "docs", "plugins.md"))) {
2194
+ await copyIfExists(join3(packageRoot, "src", "server", "docs"), join3(target, "dist", "docs"));
2195
+ }
2196
+ if (!await copyIfExists(join3(packageRoot, "docs"), join3(target, "docs"))) {
2197
+ await copyIfExists(join3(packageRoot, "src", "server", "docs"), join3(target, "docs"));
2198
+ }
2199
+ await copyIfExists(join3(packageRoot, "skills"), join3(target, "skills"));
2200
+ await copyIfExists(join3(packageRoot, "references"), join3(target, "references"));
2201
+ await copyIfExists(join3(packageRoot, "src", "globals.css"), join3(target, "src", "globals.css"));
2202
+ }
2203
+ }
2131
2204
  async function ensurePython(workspaceRoot, specs) {
2132
2205
  if (specs.length === 0) return;
2133
2206
  const venvPython = join3(workspaceRoot, ".venv", "bin", "python");
@@ -2164,6 +2237,11 @@ function assertEnvKey(key) {
2164
2237
  async function isRuntimeMaterialized(workspaceRoot, contributions) {
2165
2238
  const hasPython = contributions.some(({ provisioning }) => (provisioning.python ?? []).length > 0);
2166
2239
  if (hasPython && !await exists(join3(workspaceRoot, ".venv", "bin", "python"))) return false;
2240
+ for (const { provisioning } of contributions) {
2241
+ for (const spec of provisioning.nodePackages ?? []) {
2242
+ if (!await exists(join3(nodePackageTarget(workspaceRoot, spec.packageName), "package.json"))) return false;
2243
+ }
2244
+ }
2167
2245
  return true;
2168
2246
  }
2169
2247
  async function writeShims(workspaceRoot, env) {
@@ -2232,6 +2310,7 @@ async function provisionRuntimeWorkspace({
2232
2310
  }
2233
2311
  }
2234
2312
  await seedTemplates(workspaceRoot, active);
2313
+ await ensureNodePackages(workspaceRoot, active.flatMap(({ provisioning }) => provisioning.nodePackages ?? []));
2235
2314
  await ensurePython(workspaceRoot, active.flatMap(({ provisioning }) => provisioning.python ?? []));
2236
2315
  const actualBinDir = await writeShims(workspaceRoot, env);
2237
2316
  await mkdir4(dirname5(markerPath), { recursive: true });
@@ -2649,8 +2728,10 @@ function createServerFileSearch(workspace, sandbox) {
2649
2728
  const command = [
2650
2729
  "find .",
2651
2730
  "-maxdepth 10",
2731
+ "-path './.boring-agent' -prune -o",
2652
2732
  buildFindArgs(glob),
2653
2733
  "-type f",
2734
+ "-print",
2654
2735
  `| head -n ${safeLimit}`
2655
2736
  ].join(" ");
2656
2737
  const { stdout, exitCode } = await sandbox.exec(command, {
@@ -3274,7 +3355,6 @@ function resolveMode(mode = autoDetectMode()) {
3274
3355
 
3275
3356
  // src/server/createAgentApp.ts
3276
3357
  import Fastify from "fastify";
3277
- import { basename as basename2 } from "path";
3278
3358
 
3279
3359
  // src/server/harness/pi-coding-agent/createHarness.ts
3280
3360
  import { existsSync, readFileSync as readFileSync2 } from "fs";
@@ -3287,7 +3367,8 @@ import {
3287
3367
  ModelRegistry,
3288
3368
  DefaultResourceLoader,
3289
3369
  SettingsManager as SettingsManager2,
3290
- getAgentDir as getAgentDir2
3370
+ getAgentDir as getAgentDir2,
3371
+ loadSkills
3291
3372
  } from "@mariozechner/pi-coding-agent";
3292
3373
 
3293
3374
  // src/server/harness/pi-coding-agent/tool-adapter.ts
@@ -4216,6 +4297,17 @@ var WORKSPACE_PATHS_GUIDELINE = [
4216
4297
  function composeSystemPromptAppend(hostAppend) {
4217
4298
  return [WORKSPACE_PATHS_GUIDELINE, hostAppend?.trim()].filter(Boolean).join("\n\n");
4218
4299
  }
4300
+ function buildDynamicPromptExtension(source) {
4301
+ return (pi) => {
4302
+ pi.on("before_agent_start", async (event) => {
4303
+ const extra = (await source())?.trim();
4304
+ if (!extra) return;
4305
+ return { systemPrompt: `${event.systemPrompt}
4306
+
4307
+ ${extra}` };
4308
+ });
4309
+ };
4310
+ }
4219
4311
  function extractUserMessageText(message) {
4220
4312
  const record = message;
4221
4313
  if (record?.role !== "user") return "";
@@ -4341,6 +4433,30 @@ function createPiCodingAgentHarness(opts) {
4341
4433
  sessionDir: opts.sessionDir
4342
4434
  });
4343
4435
  const piSessions = /* @__PURE__ */ new Map();
4436
+ const effectiveSkillPaths = [];
4437
+ const effectivePackages = [];
4438
+ const effectiveExtensionPaths = [];
4439
+ const refreshEffectiveResources = () => {
4440
+ const dynamic = opts.pi?.getHotReloadableResources?.() ?? {};
4441
+ effectiveSkillPaths.splice(
4442
+ 0,
4443
+ effectiveSkillPaths.length,
4444
+ ...opts.pi?.additionalSkillPaths ?? [],
4445
+ ...dynamic.additionalSkillPaths ?? []
4446
+ );
4447
+ effectivePackages.splice(
4448
+ 0,
4449
+ effectivePackages.length,
4450
+ ...mergePiPackageSources(opts.pi?.packages ?? [], dynamic.packages ?? [])
4451
+ );
4452
+ effectiveExtensionPaths.splice(
4453
+ 0,
4454
+ effectiveExtensionPaths.length,
4455
+ ...opts.pi?.extensionPaths ?? [],
4456
+ ...dynamic.extensionPaths ?? []
4457
+ );
4458
+ };
4459
+ refreshEffectiveResources();
4344
4460
  const scheduleSessionTitle = createSessionTitleScheduler({
4345
4461
  loadSession: (sessionId) => sessionStore.load({ workspaceId: "default" }, sessionId),
4346
4462
  writeTitle: (sessionId, title) => {
@@ -4373,23 +4489,44 @@ function createPiCodingAgentHarness(opts) {
4373
4489
  }
4374
4490
  const resolvedModel = resolveRequestedModel(modelRegistry, input);
4375
4491
  const model = resolvedModel ?? resolveDefaultModel(modelRegistry);
4492
+ refreshEffectiveResources();
4376
4493
  const composedSystemPromptAppend = composeSystemPromptAppend(opts.systemPromptAppend);
4494
+ const dynamicPromptExtension = opts.systemPromptDynamic ? buildDynamicPromptExtension(opts.systemPromptDynamic) : void 0;
4377
4495
  const agentDir = getAgentDir2();
4378
- const additionalSkillPaths = opts.resourceLoaderOptions?.additionalSkillPaths ?? [];
4379
- const piPackages = opts.resourceLoaderOptions?.piPackages ?? [];
4496
+ const extensionFactories = [
4497
+ ...dynamicPromptExtension ? [dynamicPromptExtension] : [],
4498
+ ...opts.pi?.extensionFactories ?? []
4499
+ ];
4380
4500
  const settingsManager = createResourceSettingsManager(
4381
4501
  ctx.workdir,
4382
4502
  agentDir,
4383
- piPackages
4503
+ effectivePackages
4384
4504
  );
4385
4505
  const resourceLoader = new DefaultResourceLoader({
4386
4506
  cwd: ctx.workdir,
4387
4507
  agentDir,
4388
4508
  settingsManager,
4389
4509
  appendSystemPromptOverride: (base) => [...base, composedSystemPromptAppend],
4390
- ...opts.resourceLoaderOptions?.noContextFiles ? { noContextFiles: true } : {},
4391
- ...opts.resourceLoaderOptions?.noSkills ? { noSkills: true } : {},
4392
- ...additionalSkillPaths.length ? { additionalSkillPaths } : {}
4510
+ ...effectiveExtensionPaths.length ? { additionalExtensionPaths: effectiveExtensionPaths } : {},
4511
+ ...extensionFactories.length ? { extensionFactories } : {},
4512
+ ...opts.pi?.noContextFiles ? { noContextFiles: true } : {},
4513
+ ...opts.pi?.noSkills ? { noSkills: true } : {},
4514
+ ...effectiveSkillPaths.length ? { additionalSkillPaths: effectiveSkillPaths } : {},
4515
+ // skillsOverride REPLACES Pi's resolved skill set, which includes
4516
+ // skills contributed by host-declared pi packages (e.g.
4517
+ // @hachej/boring-pi → boring-plugin-authoring). Only trigger it for
4518
+ // the explicit `noSkills` opt-out, where the host wants a clean slate.
4519
+ // Passing additionalSkillPaths is not, by itself, a request to throw
4520
+ // away package skills — those should keep flowing through Pi's loader
4521
+ // and merge with the additional paths.
4522
+ ...opts.pi?.noSkills ? {
4523
+ skillsOverride: () => loadSkills({
4524
+ cwd: ctx.workdir,
4525
+ agentDir,
4526
+ skillPaths: effectiveSkillPaths,
4527
+ includeDefaults: false
4528
+ })
4529
+ } : {}
4393
4530
  });
4394
4531
  await resourceLoader?.reload();
4395
4532
  const { session: piSession } = await createAgentSession({
@@ -4414,6 +4551,13 @@ function createPiCodingAgentHarness(opts) {
4414
4551
  piSessions.set(sessionId, handle);
4415
4552
  return handle;
4416
4553
  }
4554
+ async function reloadPiSession(sessionId) {
4555
+ const handle = piSessions.get(sessionId);
4556
+ if (!handle) return false;
4557
+ refreshEffectiveResources();
4558
+ await handle.piSession.reload();
4559
+ return true;
4560
+ }
4417
4561
  function disposePiSession(sessionId) {
4418
4562
  const handle = piSessions.get(sessionId);
4419
4563
  if (!handle) return;
@@ -4526,6 +4670,7 @@ function createPiCodingAgentHarness(opts) {
4526
4670
  getSystemPrompt(sessionId) {
4527
4671
  return piSessions.get(sessionId)?.piSession.systemPrompt;
4528
4672
  },
4673
+ reloadSession: reloadPiSession,
4529
4674
  async *sendMessage(input, ctx) {
4530
4675
  const { piSession } = await getOrCreatePiSession(
4531
4676
  input.sessionId,
@@ -4926,7 +5071,7 @@ ${attachmentNotes.join("\n")}` : message,
4926
5071
 
4927
5072
  // src/server/harness/pi-coding-agent/pluginLoader.ts
4928
5073
  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";
5074
+ import { join as join7, extname as extname3, resolve as resolve4, sep as sep3, relative as relative6 } from "path";
4930
5075
  import { homedir as homedir4 } from "os";
4931
5076
  import { pathToFileURL } from "url";
4932
5077
  var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs"]);
@@ -5001,7 +5146,7 @@ async function loadNpmPlugin(pkgDir, importFn) {
5001
5146
  if (resolvedMain !== resolvedPkgDir && !resolvedMain.startsWith(resolvedPkgDir + sep3)) {
5002
5147
  return [];
5003
5148
  }
5004
- const relPath = relative5(resolvedPkgDir, resolvedMain);
5149
+ const relPath = relative6(resolvedPkgDir, resolvedMain);
5005
5150
  if (relPath.startsWith("..")) {
5006
5151
  return [];
5007
5152
  }
@@ -5086,7 +5231,7 @@ import {
5086
5231
  // src/server/tools/operations/bound.ts
5087
5232
  import { constants as constants3 } from "fs";
5088
5233
  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";
5234
+ import { dirname as dirname7, isAbsolute as isAbsolute5, relative as relative7, resolve as resolve5 } from "path";
5090
5235
  function toPosixPath(value) {
5091
5236
  return value.split("\\").join("/");
5092
5237
  }
@@ -5131,8 +5276,8 @@ function matchesGlob(relativePath, pattern) {
5131
5276
  }
5132
5277
  function shouldSkipDir(relativePath, ignore) {
5133
5278
  const normalizedRel = toPosixPath(relativePath);
5134
- const basename4 = normalizedRel.split("/").at(-1) ?? normalizedRel;
5135
- if (basename4 === ".git" || basename4 === "node_modules") return true;
5279
+ const basename3 = normalizedRel.split("/").at(-1) ?? normalizedRel;
5280
+ if (basename3 === ".git" || basename3 === "node_modules") return true;
5136
5281
  return ignore.some((pattern) => {
5137
5282
  return matchesGlob(normalizedRel, pattern) || matchesGlob(`${normalizedRel}/`, pattern);
5138
5283
  });
@@ -5143,7 +5288,7 @@ async function walkMatches(root, current, pattern, ignore, limit, out) {
5143
5288
  for (const entry of entries) {
5144
5289
  if (out.length >= limit) return;
5145
5290
  const absolutePath = resolve5(current, entry.name);
5146
- const relativePath = toPosixPath(relative6(root, absolutePath));
5291
+ const relativePath = toPosixPath(relative7(root, absolutePath));
5147
5292
  if (entry.isDirectory() && shouldSkipDir(relativePath, ignore)) continue;
5148
5293
  if (matchesGlob(relativePath, pattern)) {
5149
5294
  out.push(absolutePath);
@@ -5175,8 +5320,8 @@ async function assertWithinWorkspace(workspaceRoot, absPath) {
5175
5320
  const resolvedTarget = resolve5(dirname7(absPath), target);
5176
5321
  const nearestAncestor = await findNearestExistingAncestor(resolvedTarget);
5177
5322
  const realAncestor = await realpath2(nearestAncestor);
5178
- const rel2 = relative6(realRoot, realAncestor);
5179
- if (rel2.startsWith("..") || isAbsolute4(rel2)) {
5323
+ const rel2 = relative7(realRoot, realAncestor);
5324
+ if (rel2.startsWith("..") || isAbsolute5(rel2)) {
5180
5325
  throw new Error(`path "${absPath}" is outside workspace`);
5181
5326
  }
5182
5327
  return;
@@ -5196,16 +5341,16 @@ async function assertWithinWorkspace(workspaceRoot, absPath) {
5196
5341
  if (code === "ENOENT") {
5197
5342
  const nearestAncestor = await findNearestExistingAncestor(dirname7(absPath));
5198
5343
  const realAncestor = await realpath2(nearestAncestor);
5199
- const rel2 = relative6(realRoot, realAncestor);
5200
- if (rel2.startsWith("..") || isAbsolute4(rel2)) {
5344
+ const rel2 = relative7(realRoot, realAncestor);
5345
+ if (rel2.startsWith("..") || isAbsolute5(rel2)) {
5201
5346
  throw new Error(`path "${absPath}" is outside workspace`);
5202
5347
  }
5203
5348
  return;
5204
5349
  }
5205
5350
  throw err;
5206
5351
  }
5207
- const rel = relative6(realRoot, realCandidate);
5208
- if (rel.startsWith("..") || isAbsolute4(rel)) {
5352
+ const rel = relative7(realRoot, realCandidate);
5353
+ if (rel.startsWith("..") || isAbsolute5(rel)) {
5209
5354
  throw new Error(`path "${absPath}" is outside workspace`);
5210
5355
  }
5211
5356
  }
@@ -5296,7 +5441,7 @@ function boundFs(workspaceRoot) {
5296
5441
  }
5297
5442
 
5298
5443
  // src/server/tools/operations/vercel.ts
5299
- import { isAbsolute as isAbsolute5, relative as relative7 } from "path";
5444
+ import { isAbsolute as isAbsolute6, relative as relative8 } from "path";
5300
5445
  function rootAliases(workspace) {
5301
5446
  const aliases = [workspace.root];
5302
5447
  if (workspace.root === VERCEL_SANDBOX_WORKSPACE_ROOT) aliases.push(VERCEL_SANDBOX_REMOTE_ROOT);
@@ -5304,11 +5449,11 @@ function rootAliases(workspace) {
5304
5449
  return aliases;
5305
5450
  }
5306
5451
  function isOutsideWorkspaceRel(rel) {
5307
- return rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute5(rel);
5452
+ return rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute6(rel);
5308
5453
  }
5309
5454
  function toRelPath(workspace, absolutePath) {
5310
5455
  for (const root of rootAliases(workspace)) {
5311
- const rel = relative7(root, absolutePath);
5456
+ const rel = relative8(root, absolutePath);
5312
5457
  if (!isOutsideWorkspaceRel(rel)) return rel;
5313
5458
  }
5314
5459
  const skillMarker = "/.agents/skills/";
@@ -5491,7 +5636,7 @@ import {
5491
5636
  truncateHead,
5492
5637
  truncateLine
5493
5638
  } from "@mariozechner/pi-coding-agent";
5494
- import { resolve as resolve6, relative as relative8 } from "path";
5639
+ import { resolve as resolve6, relative as relative9 } from "path";
5495
5640
 
5496
5641
  // src/server/catalog/tools/_shared.ts
5497
5642
  function makeError(message) {
@@ -5644,7 +5789,7 @@ function vercelGrepTool(sandbox, workspaceRoot) {
5644
5789
  }
5645
5790
  if (workspaceRoot && typeof params.path === "string" && params.path.length > 0) {
5646
5791
  const resolved = resolve6(workspaceRoot, params.path);
5647
- const rel = relative8(workspaceRoot, resolved);
5792
+ const rel = relative9(workspaceRoot, resolved);
5648
5793
  if (rel.startsWith("..") || rel.startsWith("/")) {
5649
5794
  return makeError(`path "${params.path}" is outside workspace`);
5650
5795
  }
@@ -5804,8 +5949,7 @@ function createExecuteIsolatedCodeTool(sandbox) {
5804
5949
  language: { type: "string", enum: ["python", "shell"] },
5805
5950
  image: { type: "string" },
5806
5951
  packages: { type: "array", items: { type: "string" } },
5807
- sandboxId: { type: "string" },
5808
- vmSize: { type: "string", enum: ["xxs", "xs", "s", "m", "l"] }
5952
+ sandboxId: { type: "string" }
5809
5953
  },
5810
5954
  required: ["code", "language"],
5811
5955
  additionalProperties: false
@@ -5831,8 +5975,7 @@ function createExecuteIsolatedCodeTool(sandbox) {
5831
5975
  language,
5832
5976
  image: typeof input.image === "string" ? input.image : void 0,
5833
5977
  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
5978
+ sandboxId: typeof input.sandboxId === "string" ? input.sandboxId : void 0
5836
5979
  });
5837
5980
  return {
5838
5981
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -6751,7 +6894,7 @@ function modelsRoutes(app, _opts, done) {
6751
6894
  import {
6752
6895
  DefaultPackageManager,
6753
6896
  getAgentDir as getAgentDir3,
6754
- loadSkills
6897
+ loadSkills as loadSkills2
6755
6898
  } from "@mariozechner/pi-coding-agent";
6756
6899
  var CACHE_TTL_MS2 = 3e4;
6757
6900
  function skillsRoutes(app, opts, done) {
@@ -6786,7 +6929,7 @@ function skillsRoutes(app, opts, done) {
6786
6929
  const resolved = await packageManager.resolve();
6787
6930
  return resolved.skills.filter((resource) => resource.enabled).map((resource) => resource.path);
6788
6931
  })();
6789
- const result = loadSkills({
6932
+ const result = loadSkills2({
6790
6933
  cwd: workspaceRoot,
6791
6934
  agentDir,
6792
6935
  skillPaths: [...packageSkillPaths, ...additionalSkillPaths ?? []],
@@ -7329,6 +7472,33 @@ data: ${JSON.stringify(event)}
7329
7472
  done();
7330
7473
  }
7331
7474
 
7475
+ // src/server/http/routes/reload.ts
7476
+ function reloadRoutes(app, opts, done) {
7477
+ app.post("/api/v1/agent/reload", async (request, reply) => {
7478
+ if (!opts.harness.reloadSession) {
7479
+ return reply.status(501).send({ ok: false, error: "Agent harness does not support reload" });
7480
+ }
7481
+ const sessionId = request.body?.sessionId || opts.defaultSessionId;
7482
+ try {
7483
+ const hookResult = await opts.beforeReload?.();
7484
+ const reloaded = await opts.harness.reloadSession(sessionId);
7485
+ const restart_warnings = hookResult?.restart_warnings;
7486
+ const diagnostics = hookResult?.diagnostics;
7487
+ return {
7488
+ ok: true,
7489
+ sessionId,
7490
+ reloaded,
7491
+ ...restart_warnings && restart_warnings.length > 0 ? { restart_warnings } : {},
7492
+ ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
7493
+ };
7494
+ } catch (error) {
7495
+ const message = error instanceof Error ? error.message : String(error);
7496
+ return reply.status(422).send({ ok: false, error: message });
7497
+ }
7498
+ });
7499
+ done();
7500
+ }
7501
+
7332
7502
  // src/server/http/routes/search.ts
7333
7503
  var MAX_GLOB_LENGTH = 256;
7334
7504
  var DEFAULT_LIMIT3 = 500;
@@ -7474,13 +7644,21 @@ async function createAgentApp(opts = {}) {
7474
7644
  ...opts.extraTools ?? [],
7475
7645
  ...pluginTools
7476
7646
  ];
7477
- const harness = createPiCodingAgentHarness({
7647
+ const harnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
7648
+ ...input,
7649
+ pi: {
7650
+ noContextFiles: true,
7651
+ noSkills: true,
7652
+ ...opts.pi
7653
+ }
7654
+ }));
7655
+ const harness = await harnessFactory({
7478
7656
  tools,
7479
7657
  cwd: workspaceRoot,
7480
7658
  sessionNamespace: opts.sessionNamespace,
7481
7659
  sessionDir: opts.sessionDir,
7482
7660
  systemPromptAppend: opts.systemPromptAppend,
7483
- resourceLoaderOptions: opts.resourceLoaderOptions
7661
+ systemPromptDynamic: opts.systemPromptDynamic
7484
7662
  });
7485
7663
  const sessionChangesTracker = new InMemorySessionChangesTracker();
7486
7664
  const readyTracker = new ReadyStatusTracker({
@@ -7519,10 +7697,13 @@ async function createAgentApp(opts = {}) {
7519
7697
  await app.register(modelsRoutes);
7520
7698
  await app.register(skillsRoutes, {
7521
7699
  workspaceRoot,
7522
- additionalSkillPaths: opts.resourceLoaderOptions?.additionalSkillPaths
7700
+ additionalSkillPaths: opts.pi?.additionalSkillPaths,
7701
+ piPackages: opts.pi?.packages,
7702
+ noSkills: opts.pi?.noSkills
7523
7703
  });
7524
7704
  await app.register(sessionChangesRoutes, { tracker: sessionChangesTracker });
7525
7705
  await app.register(catalogRoutes, { tools });
7706
+ await app.register(reloadRoutes, { harness, defaultSessionId: sessionId, beforeReload: opts.beforeReload });
7526
7707
  await app.register(readyStatusRoutes, { tracker: readyTracker });
7527
7708
  return app;
7528
7709
  }
@@ -7552,7 +7733,7 @@ function applyCspHeaders(response, opts = {}) {
7552
7733
  }
7553
7734
 
7554
7735
  // src/server/registerAgentRoutes.ts
7555
- import { basename as basename3 } from "path";
7736
+ import { basename as basename2 } from "path";
7556
7737
  import { AuthStorage as AuthStorage3, ModelRegistry as ModelRegistry3 } from "@mariozechner/pi-coding-agent";
7557
7738
 
7558
7739
  // src/server/catalog/mergeTools.ts
@@ -7699,7 +7880,7 @@ var DEFAULT_WORKSPACE_ID3 = "default";
7699
7880
  var STANDARD_AGENT_TOOL_NAMES = ["bash", "read", "write", "edit", "find", "grep", "ls"];
7700
7881
  var VERCEL_BINDING_HEALTHCHECK_INTERVAL_MS = 15e3;
7701
7882
  function pluginNameFromPath(path4) {
7702
- const fileName = basename3(path4);
7883
+ const fileName = basename2(path4);
7703
7884
  if (fileName.endsWith(".mjs")) return fileName.slice(0, -4);
7704
7885
  if (fileName.endsWith(".js")) return fileName.slice(0, -3);
7705
7886
  return fileName;
@@ -7758,7 +7939,7 @@ var registerAgentRoutes = async (app, opts) => {
7758
7939
  app.addHook("onClose", async () => {
7759
7940
  await modeAdapter.dispose?.();
7760
7941
  });
7761
- const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getResourceLoaderOptions === "function" || typeof opts.getSessionNamespace === "function";
7942
+ const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getPi === "function" || typeof opts.getSessionNamespace === "function";
7762
7943
  const sessionChangesTracker = new InMemorySessionChangesTracker();
7763
7944
  const runtimeBindings = /* @__PURE__ */ new Map();
7764
7945
  const MAX_RUNTIME_BINDINGS = 256;
@@ -7772,27 +7953,27 @@ var registerAgentRoutes = async (app, opts) => {
7772
7953
  async function resolveRuntimeScope(workspaceId, request) {
7773
7954
  const root = request && opts.getWorkspaceRoot ? await opts.getWorkspaceRoot(workspaceId, request) : workspaceRoot;
7774
7955
  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;
7956
+ const pi = opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot: root, request }) : opts.pi;
7776
7957
  const sessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot: root, request }) : opts.sessionNamespace);
7777
7958
  return {
7778
7959
  root,
7779
7960
  templatePath: scopedTemplatePath,
7780
- resourceLoaderOptions,
7961
+ pi,
7781
7962
  sessionNamespace,
7782
7963
  key: JSON.stringify([
7783
7964
  resolvedMode,
7784
7965
  workspaceId,
7785
7966
  root,
7786
7967
  scopedTemplatePath ?? null,
7787
- resourceLoaderOptions ?? null,
7968
+ pi ?? null,
7788
7969
  sessionNamespace ?? null
7789
7970
  ])
7790
7971
  };
7791
7972
  }
7792
7973
  async function resolveSkillScope(workspaceId, request) {
7793
7974
  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 };
7975
+ const pi = opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot: root, request }) : opts.pi;
7976
+ return { root, pi };
7796
7977
  }
7797
7978
  async function createRuntimeBinding(workspaceId, scope, request) {
7798
7979
  const root = scope.root;
@@ -7837,12 +8018,19 @@ var registerAgentRoutes = async (app, opts) => {
7837
8018
  pluginTools,
7838
8019
  logger: app.log
7839
8020
  });
7840
- const harness = createPiCodingAgentHarness({
8021
+ const harnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
8022
+ ...input,
8023
+ pi: {
8024
+ noContextFiles: true,
8025
+ noSkills: true,
8026
+ ...scope.pi
8027
+ }
8028
+ }));
8029
+ const harness = await harnessFactory({
7841
8030
  tools,
7842
8031
  cwd: root,
7843
8032
  sessionNamespace: scope.sessionNamespace,
7844
- systemPromptAppend: opts.systemPromptAppend,
7845
- resourceLoaderOptions: scope.resourceLoaderOptions
8033
+ systemPromptAppend: opts.systemPromptAppend
7846
8034
  });
7847
8035
  const readyTracker = new ReadyStatusTracker({
7848
8036
  sandboxReady: resolvedMode !== "vercel-sandbox",
@@ -8025,13 +8213,13 @@ var registerAgentRoutes = async (app, opts) => {
8025
8213
  await app.register(modelsRoutes);
8026
8214
  await app.register(skillsRoutes, {
8027
8215
  workspaceRoot,
8028
- additionalSkillPaths: opts.resourceLoaderOptions?.additionalSkillPaths,
8029
- piPackages: opts.resourceLoaderOptions?.piPackages,
8030
- noSkills: opts.resourceLoaderOptions?.noSkills,
8216
+ additionalSkillPaths: opts.pi?.additionalSkillPaths,
8217
+ piPackages: opts.pi?.packages,
8218
+ noSkills: opts.pi?.noSkills,
8031
8219
  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
8220
+ getAdditionalSkillPaths: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).pi?.additionalSkillPaths,
8221
+ getPiPackages: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).pi?.packages,
8222
+ getNoSkills: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).pi?.noSkills
8035
8223
  });
8036
8224
  await app.register(sessionChangesRoutes, { tracker: sessionChangesTracker });
8037
8225
  await app.register(
@@ -8062,6 +8250,7 @@ export {
8062
8250
  createDirectSandbox,
8063
8251
  createLogger,
8064
8252
  createNodeWorkspace,
8253
+ createResourceSettingsManager,
8065
8254
  createVercelDeploymentSnapshotProvider,
8066
8255
  createVercelSandboxWorkspace,
8067
8256
  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
 
@@ -9,9 +9,10 @@ import {
9
9
  import {
10
10
  DEFAULT_AGENT_RUNTIME_CAPABILITIES,
11
11
  PI_AGENT_RUNTIME_CAPABILITIES,
12
+ WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT,
12
13
  extractToolUiMetadata,
13
14
  isToolUiMetadata
14
- } from "../chunk-F3CE5CNW.js";
15
+ } from "../chunk-B5JECXMG.js";
15
16
 
16
17
  // src/shared/config-schema.ts
17
18
  import { z } from "zod";
@@ -77,6 +78,7 @@ export {
77
78
  ErrorLogFieldsSchema,
78
79
  PI_AGENT_RUNTIME_CAPABILITIES,
79
80
  RuntimeModeSchema,
81
+ WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT,
80
82
  extractToolUiMetadata,
81
83
  isToolUiMetadata,
82
84
  validateConfig,
@@ -0,0 +1,20 @@
1
+ interface ToolUiMetadata {
2
+ /** Renderer key registered by the app shell, e.g. `pi-subagent`. */
3
+ rendererId?: string;
4
+ /** Optional grouping label for future tool timelines/summaries. */
5
+ displayGroup?: string;
6
+ /** Optional icon hint. Frontends decide whether/how to honor it. */
7
+ icon?: string;
8
+ /** Renderer-specific structured data. */
9
+ details?: unknown;
10
+ }
11
+ declare function isToolUiMetadata(value: unknown): value is ToolUiMetadata;
12
+ /**
13
+ * Extract structured UI metadata from a ToolResult-like output.
14
+ *
15
+ * Shape:
16
+ * output.details.ui = { rendererId, displayGroup, icon, details }
17
+ */
18
+ declare function extractToolUiMetadata(output: unknown): ToolUiMetadata | undefined;
19
+
20
+ export { type ToolUiMetadata as T, extractToolUiMetadata as e, isToolUiMetadata as i };