@hachej/boring-agent 0.1.5 → 0.1.6

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.
@@ -2,6 +2,11 @@ interface Workspace {
2
2
  readonly root: string;
3
3
  readFile(relPath: string): Promise<string>;
4
4
  writeFile(relPath: string, data: string): Promise<void>;
5
+ /**
6
+ * Optional binary write operation for user-uploaded assets. Shared callers use
7
+ * Uint8Array so browser-safe workspace contracts do not depend on Node-only types.
8
+ */
9
+ writeBinaryFile?(relPath: string, data: Uint8Array): Promise<void>;
5
10
  /**
6
11
  * Optional optimized read+metadata operation. Remote workspaces should
7
12
  * implement this as one round trip when possible.
@@ -15,6 +20,8 @@ interface Workspace {
15
20
  * implement this as one round trip when possible.
16
21
  */
17
22
  writeFileWithStat?(relPath: string, data: string): Promise<Stat>;
23
+ /** Optional optimized binary write+metadata operation. */
24
+ writeBinaryFileWithStat?(relPath: string, data: Uint8Array): Promise<Stat>;
18
25
  unlink(relPath: string): Promise<void>;
19
26
  readdir(relPath: string): Promise<Entry[]>;
20
27
  stat(relPath: string): Promise<Stat>;
@@ -1,4 +1,4 @@
1
- import { S as Sandbox, f as SandboxHandleStore, e as SandboxHandleRecord, W as Workspace, F as FileSearch, A as AgentTool } from '../sandbox-handle-store-OPdvRwie.js';
1
+ import { S as Sandbox, f as SandboxHandleStore, e as SandboxHandleRecord, W as Workspace, F as FileSearch, A as AgentTool } from '../sandbox-handle-store-pNB_27Sd.js';
2
2
  import { Sandbox as Sandbox$1 } from '@vercel/sandbox';
3
3
  import { FastifyInstance, FastifyRequest, FastifyPluginAsync } from 'fastify';
4
4
  import { PackageSource } from '@mariozechner/pi-coding-agent';
@@ -289,6 +289,11 @@ interface RegisterAgentRoutesOptions {
289
289
  workspaceRoot?: string;
290
290
  sessionId?: string;
291
291
  templatePath?: string;
292
+ getTemplatePath?: (ctx: {
293
+ workspaceId: string;
294
+ workspaceRoot: string;
295
+ request?: FastifyRequest;
296
+ }) => string | undefined | Promise<string | undefined>;
292
297
  mode?: RuntimeModeId;
293
298
  version?: string;
294
299
  extraTools?: AgentTool[];
@@ -725,6 +725,11 @@ function logSandboxStop(logger, metadata) {
725
725
  ...metadata
726
726
  });
727
727
  }
728
+ function isSandboxAlreadyExistsError(error) {
729
+ const code = error?.json?.error?.code;
730
+ const message = error?.json?.error?.message;
731
+ return code === "bad_request" && typeof message === "string" && message.includes("already exists");
732
+ }
728
733
  async function createFresh(workspaceId, snapshotId, tarballUrl, previous, store, vercel, logger) {
729
734
  let sandbox;
730
735
  let sourceType = "empty";
@@ -733,21 +738,25 @@ async function createFresh(workspaceId, snapshotId, tarballUrl, previous, store,
733
738
  persistent: true,
734
739
  snapshotExpiration: 0
735
740
  };
736
- if (snapshotId) {
737
- sourceType = "snapshot";
738
- sandbox = await vercel.create({
739
- ...base,
740
- source: { type: "snapshot", snapshotId }
741
- });
742
- } else if (tarballUrl) {
743
- sourceType = "tarball";
744
- sandbox = await vercel.create({
745
- ...base,
746
- source: { type: "tarball", url: tarballUrl }
747
- });
748
- } else {
749
- sandbox = await vercel.create(base);
750
- }
741
+ const tryCreate = async () => {
742
+ try {
743
+ if (snapshotId) {
744
+ sourceType = "snapshot";
745
+ return await vercel.create({ ...base, source: { type: "snapshot", snapshotId } });
746
+ } else if (tarballUrl) {
747
+ sourceType = "tarball";
748
+ return await vercel.create({ ...base, source: { type: "tarball", url: tarballUrl } });
749
+ } else {
750
+ return await vercel.create(base);
751
+ }
752
+ } catch (error) {
753
+ if (isSandboxAlreadyExistsError(error)) {
754
+ return await vercel.get({ name: base.name, sandboxId: base.name, resume: true });
755
+ }
756
+ throw error;
757
+ }
758
+ };
759
+ sandbox = await tryCreate();
751
760
  logSandboxCreate(logger, {
752
761
  workspaceId,
753
762
  sandboxId: getSandboxIdentifier(sandbox),
@@ -1384,6 +1393,10 @@ function createNodeWorkspace(root) {
1384
1393
  const absPath = await ensureWritableWorkspacePath(root, relPath);
1385
1394
  await writeFile3(absPath, data, "utf-8");
1386
1395
  },
1396
+ async writeBinaryFile(relPath, data) {
1397
+ const absPath = await ensureWritableWorkspacePath(root, relPath);
1398
+ await writeFile3(absPath, data);
1399
+ },
1387
1400
  async readFileWithStat(relPath) {
1388
1401
  const absPath = await ensureExistingWorkspacePath(root, relPath);
1389
1402
  const [content, fileStat] = await Promise.all([
@@ -1409,6 +1422,16 @@ function createNodeWorkspace(root) {
1409
1422
  kind: fileStat.isDirectory() ? "dir" : "file"
1410
1423
  };
1411
1424
  },
1425
+ async writeBinaryFileWithStat(relPath, data) {
1426
+ const absPath = await ensureWritableWorkspacePath(root, relPath);
1427
+ await writeFile3(absPath, data);
1428
+ const fileStat = await stat2(absPath);
1429
+ return {
1430
+ size: fileStat.size,
1431
+ mtimeMs: fileStat.mtimeMs,
1432
+ kind: fileStat.isDirectory() ? "dir" : "file"
1433
+ };
1434
+ },
1412
1435
  async unlink(relPath) {
1413
1436
  const absPath = await ensureExistingWorkspacePath(root, relPath);
1414
1437
  const pathStat = await lstat2(absPath);
@@ -1831,6 +1854,18 @@ function createVercelSandboxWorkspace(sandbox, workspaceOpts = {}) {
1831
1854
  workspaceOpts.onMutation?.();
1832
1855
  emitChange({ op: "write", path: relPath });
1833
1856
  },
1857
+ async writeBinaryFile(relPath, data) {
1858
+ const sandboxPath = toSandboxPath(relPath);
1859
+ await sandbox.writeFiles([
1860
+ {
1861
+ path: sandboxPath,
1862
+ content: Buffer.from(data)
1863
+ }
1864
+ ]);
1865
+ invalidateMetadataCache();
1866
+ workspaceOpts.onMutation?.();
1867
+ emitChange({ op: "write", path: relPath });
1868
+ },
1834
1869
  async readFileWithStat(relPath) {
1835
1870
  const sandboxPath = toSandboxPath(relPath);
1836
1871
  const cachedStat = statCache.get(sandboxPath);
@@ -1905,6 +1940,44 @@ function createVercelSandboxWorkspace(sandbox, workspaceOpts = {}) {
1905
1940
  emitChange({ op: "write", path: relPath, mtimeMs: writtenStat.mtimeMs });
1906
1941
  return cloneStat(writtenStat);
1907
1942
  },
1943
+ async writeBinaryFileWithStat(relPath, data) {
1944
+ const sandboxPath = toSandboxPath(relPath);
1945
+ const payload = Buffer.from(data);
1946
+ if (payload.byteLength > MAX_INLINE_WRITE_BYTES) {
1947
+ await sandbox.writeFiles([
1948
+ {
1949
+ path: sandboxPath,
1950
+ content: payload
1951
+ }
1952
+ ]);
1953
+ invalidateMetadataCache();
1954
+ workspaceOpts.onMutation?.();
1955
+ const writtenStat2 = remote.fs?.stat ? await (async () => {
1956
+ const fileStat = await remote.fs.stat(sandboxPath);
1957
+ return {
1958
+ size: fileStat.size,
1959
+ mtimeMs: fileStat.mtimeMs,
1960
+ kind: fileStat.isDirectory() ? "dir" : "file"
1961
+ };
1962
+ })() : await runJson(
1963
+ remote,
1964
+ `node -e ${shellQuote2(`const fs=require('fs'); const p=process.argv[1]; const s=fs.statSync(p); process.stdout.write(JSON.stringify({size:s.size,mtimeMs:s.mtimeMs,kind:s.isDirectory()?'dir':'file'}))`)} ${shellQuote2(sandboxPath)}`
1965
+ );
1966
+ statCache.set(sandboxPath, writtenStat2);
1967
+ emitChange({ op: "write", path: relPath, mtimeMs: writtenStat2.mtimeMs });
1968
+ return cloneStat(writtenStat2);
1969
+ }
1970
+ const encoded = payload.toString("base64");
1971
+ const writtenStat = await runJson(
1972
+ remote,
1973
+ `node -e ${shellQuote2(`const fs=require('fs'); const p=process.argv[1]; const data=Buffer.from(process.argv[2],'base64'); fs.writeFileSync(p,data); const s=fs.statSync(p); process.stdout.write(JSON.stringify({size:s.size,mtimeMs:s.mtimeMs,kind:s.isDirectory()?'dir':'file'}))`)} ${shellQuote2(sandboxPath)} ${shellQuote2(encoded)}`
1974
+ );
1975
+ invalidateMetadataCache();
1976
+ statCache.set(sandboxPath, writtenStat);
1977
+ workspaceOpts.onMutation?.();
1978
+ emitChange({ op: "write", path: relPath, mtimeMs: writtenStat.mtimeMs });
1979
+ return cloneStat(writtenStat);
1980
+ },
1908
1981
  async unlink(relPath) {
1909
1982
  const sandboxPath = toSandboxPath(relPath);
1910
1983
  if (remote.fs?.rm) await remote.fs.rm(sandboxPath, { recursive: false, force: false });
@@ -2410,6 +2483,32 @@ async function ensureVercelWorkspaceRoot(sandbox) {
2410
2483
  throw new Error(`failed to initialize ${VERCEL_SANDBOX_REMOTE_ROOT} (exit ${result.exitCode ?? "unknown"})`);
2411
2484
  }
2412
2485
  }
2486
+ async function seedTemplateIntoVercelWorkspace(workspace, templatePath, logger) {
2487
+ const files = await collectFiles(templatePath);
2488
+ const hash = computeTemplateHash(files);
2489
+ const markerPath = `.boring-agent/templates/${hash}.json`;
2490
+ try {
2491
+ await workspace.stat(markerPath);
2492
+ logger.info("[vercel-sandbox:mode] template already seeded", {
2493
+ hash,
2494
+ fileCount: files.length
2495
+ });
2496
+ return;
2497
+ } catch {
2498
+ }
2499
+ for (const file of files) {
2500
+ if (workspace.writeBinaryFile) {
2501
+ await workspace.writeBinaryFile(file.rel, new Uint8Array(file.content));
2502
+ } else {
2503
+ await workspace.writeFile(file.rel, file.content.toString("utf-8"));
2504
+ }
2505
+ }
2506
+ await workspace.writeFile(markerPath, JSON.stringify({ hash, seededAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
2507
+ logger.info("[vercel-sandbox:mode] template seeded into workspace", {
2508
+ hash,
2509
+ fileCount: files.length
2510
+ });
2511
+ }
2413
2512
  var DEFAULT_MODE_LOGGER = {
2414
2513
  info(message, metadata) {
2415
2514
  process.stderr.write(`${message} ${JSON.stringify(metadata)}
@@ -2521,17 +2620,13 @@ function createVercelSandboxModeAdapter(opts = {}) {
2521
2620
  onMutation: markDirty
2522
2621
  });
2523
2622
  await ensureVercelWorkspaceRoot(sandboxHandle);
2524
- if (ctx.templatePath && !tarballUrl) {
2525
- logger.info("[vercel-sandbox:mode] falling back to writeFiles for template", {
2526
- templatePath: ctx.templatePath
2527
- });
2528
- const files = await collectFiles(ctx.templatePath);
2529
- for (const file of files) {
2530
- await workspace.writeFile(file.rel, file.content.toString("utf-8"));
2623
+ if (ctx.templatePath) {
2624
+ if (!tarballUrl) {
2625
+ logger.info("[vercel-sandbox:mode] falling back to writeFiles for template", {
2626
+ templatePath: ctx.templatePath
2627
+ });
2531
2628
  }
2532
- logger.info("[vercel-sandbox:mode] writeFiles fallback complete", {
2533
- fileCount: files.length
2534
- });
2629
+ await seedTemplateIntoVercelWorkspace(workspace, ctx.templatePath, logger);
2535
2630
  }
2536
2631
  const sandbox = createVercelSandboxExec(sandboxHandle, {
2537
2632
  onMutation: markDirty
@@ -4920,6 +5015,13 @@ function healthRoutes(app, opts, done) {
4920
5015
  }
4921
5016
 
4922
5017
  // src/server/http/routes/file.ts
5018
+ import { dirname as dirname7, extname as extname2, relative as relative7 } from "path/posix";
5019
+ var BORING_SETTINGS_PATH = ".boring/settings";
5020
+ var DEFAULT_MARKDOWN_IMAGE_UPLOAD_DIR = "assets/images";
5021
+ var MAX_UPLOAD_BYTES = 10 * 1024 * 1024;
5022
+ function defaultWorkspaceSettings() {
5023
+ return { markdown: { imageUploadDir: DEFAULT_MARKDOWN_IMAGE_UPLOAD_DIR } };
5024
+ }
4923
5025
  function isPathValidationError(err) {
4924
5026
  return err instanceof Error && typeof err.reason === "string";
4925
5027
  }
@@ -4965,6 +5067,58 @@ function requireStringParam(value, field, reply) {
4965
5067
  }
4966
5068
  return value;
4967
5069
  }
5070
+ function parseWorkspaceSettings(raw) {
5071
+ try {
5072
+ const parsed = JSON.parse(raw);
5073
+ const dir = parsed?.markdown?.imageUploadDir;
5074
+ return {
5075
+ ...parsed,
5076
+ markdown: {
5077
+ ...parsed.markdown ?? {},
5078
+ imageUploadDir: typeof dir === "string" && dir.trim() ? dir.trim() : DEFAULT_MARKDOWN_IMAGE_UPLOAD_DIR
5079
+ }
5080
+ };
5081
+ } catch {
5082
+ return defaultWorkspaceSettings();
5083
+ }
5084
+ }
5085
+ async function readWorkspaceSettings(workspace) {
5086
+ try {
5087
+ return parseWorkspaceSettings(await workspace.readFile(BORING_SETTINGS_PATH));
5088
+ } catch (error) {
5089
+ const code = error?.code;
5090
+ if (code === "ENOENT") return defaultWorkspaceSettings();
5091
+ throw error;
5092
+ }
5093
+ }
5094
+ function normalizeUploadDir(value) {
5095
+ if (typeof value !== "string") return null;
5096
+ const dir = value.trim().replace(/^\.\/+/, "").replace(/\/+/g, "/");
5097
+ if (!dir || dir.includes("\0") || dir.startsWith("/") || dir.split("/").includes("..")) return null;
5098
+ return dir.replace(/\/+$/, "");
5099
+ }
5100
+ function extForUpload(filename, contentType) {
5101
+ const fromName = extname2(filename).toLowerCase().replace(/^\./, "");
5102
+ if (/^[a-z0-9]{1,12}$/.test(fromName)) return fromName;
5103
+ if (contentType === "image/jpeg") return "jpg";
5104
+ if (contentType === "image/png") return "png";
5105
+ if (contentType === "image/gif") return "gif";
5106
+ if (contentType === "image/webp") return "webp";
5107
+ if (contentType === "image/svg+xml") return "svg";
5108
+ return "bin";
5109
+ }
5110
+ function basenameForUpload(filename) {
5111
+ const base = filename.split("/").pop()?.split("\\").pop() ?? "image";
5112
+ const withoutExt = base.replace(/\.[^.]*$/, "");
5113
+ const safe = withoutExt.normalize("NFKD").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
5114
+ return safe || "image";
5115
+ }
5116
+ function markdownUrlFor(sourcePath, assetPath) {
5117
+ if (!sourcePath) return assetPath;
5118
+ const fromDir = dirname7(sourcePath.replace(/\\/g, "/"));
5119
+ const rel = relative7(fromDir === "." ? "" : fromDir, assetPath);
5120
+ return rel && !rel.startsWith(".") ? rel : rel || assetPath;
5121
+ }
4968
5122
  function fileRoutes(app, opts, done) {
4969
5123
  async function resolveWorkspace(request) {
4970
5124
  if (opts.getWorkspace) return await opts.getWorkspace(request);
@@ -5041,6 +5195,87 @@ function fileRoutes(app, opts, done) {
5041
5195
  return classifyError(err, reply, "file");
5042
5196
  }
5043
5197
  });
5198
+ app.post("/api/v1/files/upload", async (request, reply) => {
5199
+ const body = request.body;
5200
+ const filename = requireStringParam(body?.filename, "filename", reply);
5201
+ if (filename === null) return;
5202
+ const contentBase64 = requireStringParam(body?.contentBase64, "contentBase64", reply);
5203
+ if (contentBase64 === null) return;
5204
+ const contentType = typeof body.contentType === "string" ? body.contentType.trim().toLowerCase() : "";
5205
+ if (!contentType.startsWith("image/")) {
5206
+ return reply.code(400).send({
5207
+ error: { code: ERROR_CODE_VALIDATION_ERROR, message: "contentType must be an image/* MIME type", field: "contentType" }
5208
+ });
5209
+ }
5210
+ try {
5211
+ const workspace = await resolveWorkspace(request);
5212
+ const settings = await readWorkspaceSettings(workspace);
5213
+ const dir = normalizeUploadDir(body.directory) ?? normalizeUploadDir(settings.markdown?.imageUploadDir) ?? DEFAULT_MARKDOWN_IMAGE_UPLOAD_DIR;
5214
+ const ext = extForUpload(filename, contentType);
5215
+ const base = basenameForUpload(filename);
5216
+ const unique = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
5217
+ const path4 = `${dir}/${base}-${unique}.${ext}`;
5218
+ const bytes = Buffer.from(contentBase64, "base64");
5219
+ if (bytes.byteLength === 0 || bytes.byteLength > MAX_UPLOAD_BYTES) {
5220
+ return reply.code(400).send({
5221
+ error: { code: ERROR_CODE_VALIDATION_ERROR, message: `upload must be 1 byte to ${MAX_UPLOAD_BYTES} bytes`, field: "contentBase64" }
5222
+ });
5223
+ }
5224
+ await workspace.mkdir(dir, { recursive: true });
5225
+ const stat7 = workspace.writeBinaryFileWithStat ? await workspace.writeBinaryFileWithStat(path4, bytes) : await (async () => {
5226
+ if (!workspace.writeBinaryFile) {
5227
+ throw new Error("workspace does not support binary uploads");
5228
+ }
5229
+ await workspace.writeBinaryFile(path4, bytes);
5230
+ return await workspace.stat(path4);
5231
+ })();
5232
+ const sourcePath = typeof body.sourcePath === "string" && !body.sourcePath.includes("\0") ? body.sourcePath : null;
5233
+ return {
5234
+ ok: true,
5235
+ path: path4,
5236
+ markdownUrl: markdownUrlFor(sourcePath, path4),
5237
+ mtimeMs: stat7.kind === "file" ? stat7.mtimeMs : void 0
5238
+ };
5239
+ } catch (err) {
5240
+ return classifyError(err, reply, "upload");
5241
+ }
5242
+ });
5243
+ app.get("/api/v1/workspace-settings", async (request, reply) => {
5244
+ try {
5245
+ const workspace = await resolveWorkspace(request);
5246
+ return { settings: await readWorkspaceSettings(workspace) };
5247
+ } catch (err) {
5248
+ return classifyError(err, reply, "settings");
5249
+ }
5250
+ });
5251
+ app.put("/api/v1/workspace-settings", async (request, reply) => {
5252
+ const body = request.body;
5253
+ const incoming = body?.settings ?? body;
5254
+ const markdown = incoming.markdown ?? {};
5255
+ const imageUploadDir = normalizeUploadDir(markdown.imageUploadDir);
5256
+ if (!imageUploadDir) {
5257
+ return reply.code(400).send({
5258
+ error: { code: ERROR_CODE_INVALID_PATH, message: "markdown.imageUploadDir must be a relative workspace path", field: "markdown.imageUploadDir" }
5259
+ });
5260
+ }
5261
+ try {
5262
+ const workspace = await resolveWorkspace(request);
5263
+ const current = await readWorkspaceSettings(workspace);
5264
+ const next = {
5265
+ ...current,
5266
+ markdown: {
5267
+ ...current.markdown ?? {},
5268
+ imageUploadDir
5269
+ }
5270
+ };
5271
+ await workspace.mkdir(".boring", { recursive: true });
5272
+ await workspace.writeFile(BORING_SETTINGS_PATH, `${JSON.stringify(next, null, 2)}
5273
+ `);
5274
+ return { settings: next };
5275
+ } catch (err) {
5276
+ return classifyError(err, reply, "settings");
5277
+ }
5278
+ });
5044
5279
  app.delete("/api/v1/files", async (request, reply) => {
5045
5280
  const query = request.query;
5046
5281
  const path4 = requireStringParam(query.path, "path", reply);
@@ -5806,7 +6041,11 @@ function skillsRoutes(app, opts, done) {
5806
6041
  return reply.code(200).send({ skills: cached.skills });
5807
6042
  }
5808
6043
  try {
5809
- const result = loadSkills2({ cwd: opts.workspaceRoot, includeDefaults: true });
6044
+ const result = loadSkills2({
6045
+ cwd: opts.workspaceRoot,
6046
+ skillPaths: opts.additionalSkillPaths ?? [],
6047
+ includeDefaults: true
6048
+ });
5810
6049
  const skills = result.skills.map((s) => ({
5811
6050
  name: s.name,
5812
6051
  description: s.description
@@ -6289,7 +6528,10 @@ async function createAgentApp(opts = {}) {
6289
6528
  });
6290
6529
  await app.register(systemPromptRoutes, { harness });
6291
6530
  await app.register(modelsRoutes);
6292
- await app.register(skillsRoutes, { workspaceRoot });
6531
+ await app.register(skillsRoutes, {
6532
+ workspaceRoot,
6533
+ additionalSkillPaths: opts.resourceLoaderOptions?.additionalSkillPaths
6534
+ });
6293
6535
  await app.register(sessionChangesRoutes, { tracker: sessionChangesTracker });
6294
6536
  await app.register(catalogRoutes, { tools });
6295
6537
  await app.register(readyStatusRoutes, { tracker: readyTracker });
@@ -6420,12 +6662,13 @@ var registerAgentRoutes = async (app, opts) => {
6420
6662
  key: `${resolvedMode}:${workspaceId}:${root}`
6421
6663
  };
6422
6664
  }
6423
- async function createRuntimeBinding(workspaceId, root) {
6665
+ async function createRuntimeBinding(workspaceId, root, request) {
6666
+ const scopedTemplatePath = opts.getTemplatePath ? await opts.getTemplatePath({ workspaceId, workspaceRoot: root, request }) : templatePath;
6424
6667
  const runtimeBundle = await modeAdapter.create({
6425
6668
  workspaceRoot: root,
6426
6669
  sessionId: workspaceId,
6427
6670
  workspaceId,
6428
- templatePath
6671
+ templatePath: scopedTemplatePath
6429
6672
  });
6430
6673
  const standardTools = [
6431
6674
  ...buildHarnessAgentTools(runtimeBundle),
@@ -6494,7 +6737,7 @@ var registerAgentRoutes = async (app, opts) => {
6494
6737
  await existing
6495
6738
  );
6496
6739
  }
6497
- const created = createRuntimeBinding(workspaceId, scope.root);
6740
+ const created = createRuntimeBinding(workspaceId, scope.root, request);
6498
6741
  runtimeBindings.set(scope.key, created);
6499
6742
  try {
6500
6743
  return await ensureRuntimeBindingReady(
@@ -6631,7 +6874,10 @@ var registerAgentRoutes = async (app, opts) => {
6631
6874
  getHarness: async (request) => (await getBindingForRequest(request)).harness
6632
6875
  });
6633
6876
  await app.register(modelsRoutes);
6634
- await app.register(skillsRoutes, { workspaceRoot });
6877
+ await app.register(skillsRoutes, {
6878
+ workspaceRoot,
6879
+ additionalSkillPaths: opts.resourceLoaderOptions?.additionalSkillPaths
6880
+ });
6635
6881
  await app.register(sessionChangesRoutes, { tracker: sessionChangesTracker });
6636
6882
  await app.register(
6637
6883
  catalogRoutes,
@@ -1,6 +1,6 @@
1
1
  export { A as AgentHarness, R as RunContext, S as SendMessageInput, a as SessionCtx, b as SessionDetail, c as SessionStore, d as SessionSummary } from '../harness-CMiJ4kok.js';
2
- import { W as Workspace, S as Sandbox, F as FileSearch, A as AgentTool } from '../sandbox-handle-store-OPdvRwie.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-OPdvRwie.js';
2
+ import { W as Workspace, S as Sandbox, F as FileSearch, A as AgentTool } from '../sandbox-handle-store-pNB_27Sd.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-pNB_27Sd.js';
4
4
  export { UIMessage, UIMessageChunk } from 'ai';
5
5
  import { z } from 'zod';
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-agent",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Pane-embeddable coding agent. Ships direct/local/vercel-sandbox execution modes behind one interface.",
@@ -74,7 +74,7 @@
74
74
  "use-stick-to-bottom": "^1.1.3",
75
75
  "yaml": "^2.8.3",
76
76
  "zod": "^3.25.76",
77
- "@hachej/boring-ui-kit": "0.1.5"
77
+ "@hachej/boring-ui-kit": "0.1.6"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@opentelemetry/api": "^1.9.1",