@hachej/boring-agent 0.1.62 → 0.1.64

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.
@@ -171,7 +171,8 @@ var BoringChatPartSchema = z2.discriminatedUnion("type", [
171
171
  filename: z2.string().optional(),
172
172
  mediaType: z2.string().optional(),
173
173
  url: z2.string().optional(),
174
- path: z2.string().optional()
174
+ path: z2.string().optional(),
175
+ filesystem: z2.string().optional()
175
176
  }),
176
177
  z2.object({
177
178
  type: z2.literal("notice"),
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ComponentProps, HTMLAttributes, FormEvent, ReactNode, ComponentType } from 'react';
4
4
  import { FileUIPart, ChatStatus, UIMessage } from 'ai';
5
- import { T as ToolUiMetadata, B as BoringChatPart, d as BoringChatMessage, m as PiChatStatus, r as QueuedUserMessage, C as ChatError, P as PiChatEvent, o as PromptPayload, p as PromptReceipt, F as FollowUpPayload, i as FollowUpReceipt, Q as QueueClearPayload, q as QueueClearReceipt, I as InterruptPayload, e as CommandReceipt, S as StopPayload, s as StopReceipt } from '../piChatEvent-B6GDifo2.js';
5
+ import { T as ToolUiMetadata, B as BoringChatPart, d as BoringChatMessage, m as PiChatStatus, r as QueuedUserMessage, C as ChatError, P as PiChatEvent, o as PromptPayload, p as PromptReceipt, F as FollowUpPayload, i as FollowUpReceipt, Q as QueueClearPayload, q as QueueClearReceipt, I as InterruptPayload, e as CommandReceipt, S as StopPayload, s as StopReceipt } from '../piChatEvent-JbwYVBZj.js';
6
6
  import { e as SessionSummary } from '../chatSubmitPayload-DHqQL2wD.js';
7
7
  import { InputGroupAddon, InputGroupButton, InputGroupTextarea, Button, Collapsible, CollapsibleContent, CollapsibleTrigger } from '@hachej/boring-ui-kit';
8
8
  import { Streamdown } from 'streamdown';
@@ -650,7 +650,9 @@ interface PiChatPanelProps<TComposerBlocker extends ComposerBlocker = ComposerBl
650
650
  clientNonce: string;
651
651
  }) => void;
652
652
  onData?: (part: unknown) => void;
653
- onOpenArtifact?: (path: string) => void;
653
+ onOpenArtifact?: (path: string, options?: {
654
+ filesystem?: string;
655
+ }) => void;
654
656
  composerBlockers?: TComposerBlocker[];
655
657
  onComposerStop?: () => void;
656
658
  onComposerBlockerAction?: (blocker: TComposerBlocker, action: string) => void;
@@ -677,7 +679,10 @@ interface DebugDrawerProps {
677
679
  }
678
680
  declare function DebugDrawer({ apiBaseUrl, fetch, sessionId, messages, requestHeaders, storageScope, width, onWidthChange }: DebugDrawerProps): react_jsx_runtime.JSX.Element;
679
681
 
680
- type OpenArtifactHandler = (path: string) => void;
682
+ interface OpenArtifactOptions {
683
+ filesystem?: string;
684
+ }
685
+ type OpenArtifactHandler = (path: string, options?: OpenArtifactOptions) => void;
681
686
  interface ArtifactOpenProviderProps {
682
687
  onOpenArtifact?: OpenArtifactHandler;
683
688
  children: ReactNode;
@@ -13,7 +13,7 @@ import {
13
13
  StopReceiptSchema,
14
14
  extractToolUiMetadata,
15
15
  sanitizeToolUiMetadata
16
- } from "../chunk-G6YPXDHR.js";
16
+ } from "../chunk-UPRDF2PB.js";
17
17
  import {
18
18
  DebugDrawer,
19
19
  cn,
@@ -1534,7 +1534,7 @@ function toHeaderProps(part) {
1534
1534
  toolName: part.toolName
1535
1535
  };
1536
1536
  }
1537
- function PathLabel({ path }) {
1537
+ function PathLabel({ path, filesystem }) {
1538
1538
  const onOpen = useOpenArtifact();
1539
1539
  if (!onOpen) {
1540
1540
  return /* @__PURE__ */ jsx11("span", { className: "font-mono text-[12.5px] text-foreground/85", children: path });
@@ -1542,7 +1542,8 @@ function PathLabel({ path }) {
1542
1542
  const open = (e) => {
1543
1543
  e.preventDefault();
1544
1544
  e.stopPropagation();
1545
- onOpen(path);
1545
+ if (filesystem) onOpen(path, { filesystem });
1546
+ else onOpen(path);
1546
1547
  };
1547
1548
  const handleKeyDown = (e) => {
1548
1549
  if (e.key === "Enter" || e.key === " ") open(e);
@@ -1603,11 +1604,11 @@ function renderRuntimeNotReady(part) {
1603
1604
  function renderReadinessBlock(part) {
1604
1605
  return renderWorkspaceNotReady(part) ?? renderRuntimeNotReady(part);
1605
1606
  }
1606
- function pathTitle(prefix, path) {
1607
+ function pathTitle(prefix, path, filesystem) {
1607
1608
  return /* @__PURE__ */ jsxs10("span", { className: "flex min-w-0 items-center gap-1.5", children: [
1608
1609
  /* @__PURE__ */ jsx11("span", { className: "text-muted-foreground", children: prefix }),
1609
1610
  /* @__PURE__ */ jsx11("span", { className: "text-muted-foreground/40", "aria-hidden": "true", children: "\xB7" }),
1610
- /* @__PURE__ */ jsx11(PathLabel, { path })
1611
+ /* @__PURE__ */ jsx11(PathLabel, { path, filesystem })
1611
1612
  ] });
1612
1613
  }
1613
1614
  function renderBash2(part) {
@@ -1649,10 +1650,11 @@ function renderRead2(part) {
1649
1650
  const input = asRecord2(part.input);
1650
1651
  const output = asRecord2(part.output);
1651
1652
  const path = typeof input.path === "string" ? input.path : "";
1653
+ const filesystem = typeof input.filesystem === "string" ? input.filesystem : void 0;
1652
1654
  const content = extractTextContent(output);
1653
1655
  const lang = langFromPath(path);
1654
1656
  return /* @__PURE__ */ jsxs10(Tool2, { children: [
1655
- /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FileTextIcon, { className: "size-4 text-muted-foreground" }), title: pathTitle("read", path), ...toHeaderProps(part) }),
1657
+ /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FileTextIcon, { className: "size-4 text-muted-foreground" }), title: pathTitle("read", path, filesystem), ...toHeaderProps(part) }),
1656
1658
  /* @__PURE__ */ jsxs10(ToolContent, { children: [
1657
1659
  /* @__PURE__ */ jsx11(ToolInput, { input }),
1658
1660
  content && /* @__PURE__ */ jsxs10("section", { className: "space-y-2", children: [
@@ -1667,11 +1669,12 @@ function renderWrite2(part) {
1667
1669
  if (readiness) return readiness;
1668
1670
  const input = asRecord2(part.input);
1669
1671
  const path = typeof input.path === "string" ? input.path : "";
1672
+ const filesystem = typeof input.filesystem === "string" ? input.filesystem : void 0;
1670
1673
  const content = typeof input.content === "string" ? input.content : "";
1671
1674
  const bytes = content.length;
1672
1675
  const lang = langFromPath(path);
1673
1676
  return /* @__PURE__ */ jsxs10(Tool2, { children: [
1674
- /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FilePlus2Icon, { className: "size-4 text-muted-foreground" }), title: pathTitle("write", path), ...toHeaderProps(part) }),
1677
+ /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FilePlus2Icon, { className: "size-4 text-muted-foreground" }), title: pathTitle("write", path, filesystem), ...toHeaderProps(part) }),
1675
1678
  /* @__PURE__ */ jsx11(ToolContent, { children: /* @__PURE__ */ jsxs10(Artifact, { className: "rounded-none border-0 bg-transparent shadow-none", children: [
1676
1679
  /* @__PURE__ */ jsxs10(ArtifactHeader, { className: "border-0 px-0 pt-0 pb-2", children: [
1677
1680
  /* @__PURE__ */ jsxs10("div", { children: [
@@ -1728,10 +1731,11 @@ function renderEdit2(part) {
1728
1731
  if (readiness) return readiness;
1729
1732
  const input = asRecord2(part.input);
1730
1733
  const path = typeof input.path === "string" ? input.path : "";
1734
+ const filesystem = typeof input.filesystem === "string" ? input.filesystem : void 0;
1731
1735
  const oldString = typeof input.oldString === "string" ? input.oldString : "";
1732
1736
  const newString = typeof input.newString === "string" ? input.newString : "";
1733
1737
  return /* @__PURE__ */ jsxs10(Tool2, { children: [
1734
- /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FileDiffIcon, { className: "size-4 text-muted-foreground" }), title: pathTitle("edit", path), ...toHeaderProps(part) }),
1738
+ /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FileDiffIcon, { className: "size-4 text-muted-foreground" }), title: pathTitle("edit", path, filesystem), ...toHeaderProps(part) }),
1735
1739
  /* @__PURE__ */ jsxs10(ToolContent, { children: [
1736
1740
  /* @__PURE__ */ jsxs10("section", { className: "space-y-2", children: [
1737
1741
  /* @__PURE__ */ jsx11("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: "Diff" }),
@@ -7046,7 +7050,8 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7046
7050
  const canOpen = Boolean(openArtifact && openPath);
7047
7051
  const openAttachment = () => {
7048
7052
  if (!openPath) return;
7049
- openArtifact?.(openPath);
7053
+ if (file.filesystem) openArtifact?.(openPath, { filesystem: file.filesystem });
7054
+ else openArtifact?.(openPath);
7050
7055
  };
7051
7056
  const openAttachmentFromKeyboard = (event) => {
7052
7057
  if (event.key !== "Enter" && event.key !== " ") return;
@@ -2806,6 +2806,8 @@
2806
2806
  @layer theme, utilities;
2807
2807
  @layer theme {
2808
2808
  :root, :host {
2809
+ --font-sans: var(--boring-font-sans);
2810
+ --font-mono: var(--boring-font-mono);
2809
2811
  --color-black: #000;
2810
2812
  --spacing: 0.25rem;
2811
2813
  --container-xs: 20rem;
@@ -112,6 +112,7 @@ type BoringChatPart = {
112
112
  mediaType?: string;
113
113
  url?: string;
114
114
  path?: string;
115
+ filesystem?: string;
115
116
  } | {
116
117
  type: 'notice';
117
118
  id?: string;
@@ -444,6 +444,91 @@ interface ModeContext {
444
444
  requestId?: string;
445
445
  telemetry?: TelemetrySink;
446
446
  }
447
+ interface RuntimeFilesystemBindingOperations {
448
+ read(descriptor: {
449
+ filesystem: string;
450
+ path: string;
451
+ }): Promise<{
452
+ content: string;
453
+ metadata?: unknown;
454
+ }>;
455
+ list(descriptor: {
456
+ filesystem: string;
457
+ path: string;
458
+ }): Promise<{
459
+ entries: string[];
460
+ metadata?: unknown;
461
+ }>;
462
+ find(descriptor: {
463
+ filesystem: string;
464
+ path: string;
465
+ }, pattern: string, options?: {
466
+ limit?: number;
467
+ offset?: number;
468
+ }): Promise<{
469
+ paths: string[];
470
+ metadata?: unknown;
471
+ }>;
472
+ grep(descriptor: {
473
+ filesystem: string;
474
+ path: string;
475
+ }, pattern: string, options?: {
476
+ limit?: number;
477
+ offset?: number;
478
+ }): Promise<{
479
+ matches: Array<{
480
+ path: string;
481
+ line: number;
482
+ text: string;
483
+ }>;
484
+ metadata?: unknown;
485
+ }>;
486
+ stat(descriptor: {
487
+ filesystem: string;
488
+ path: string;
489
+ }): Promise<{
490
+ isDirectory: boolean;
491
+ metadata?: unknown;
492
+ }>;
493
+ write?(descriptor: {
494
+ filesystem: string;
495
+ path: string;
496
+ content: string;
497
+ expectedMtimeMs?: number;
498
+ }): Promise<{
499
+ mtimeMs?: number;
500
+ metadata?: unknown;
501
+ }>;
502
+ delete?(descriptor: {
503
+ filesystem: string;
504
+ path: string;
505
+ }): Promise<{
506
+ metadata?: unknown;
507
+ }>;
508
+ move?(descriptor: {
509
+ filesystem: string;
510
+ from: string;
511
+ to: string;
512
+ }): Promise<{
513
+ metadata?: unknown;
514
+ }>;
515
+ mkdir?(descriptor: {
516
+ filesystem: string;
517
+ path: string;
518
+ recursive?: boolean;
519
+ }): Promise<{
520
+ metadata?: unknown;
521
+ }>;
522
+ rejectMutation(operation: string, descriptor: {
523
+ filesystem: string;
524
+ path: string;
525
+ }): never;
526
+ }
527
+ interface RuntimeFilesystemBinding {
528
+ readonly filesystem: string;
529
+ readonly access: 'readonly' | 'readwrite';
530
+ readonly operations: RuntimeFilesystemBindingOperations;
531
+ }
447
532
  interface RuntimeBundle {
448
533
  runtimeContext?: WorkspaceRuntimeContext;
449
534
  /**
@@ -461,6 +546,8 @@ interface RuntimeBundle {
461
546
  bash?: RuntimeBashStrategy;
462
547
  /** Runtime-owned filesystem strategy, consumed by the agent filesystem tool builder. */
463
548
  filesystem?: RuntimeFilesystemStrategy;
549
+ /** Optional filesystem bindings prepared for this runtime/session. */
550
+ filesystemBindings?: RuntimeFilesystemBinding[];
464
551
  }
465
552
 
466
553
  declare const REMOTE_WORKER_RUNTIME_CWD = "/workspace";
@@ -608,6 +695,8 @@ declare function createRemoteWorkerSandbox(client: RemoteWorkerClient): Sandbox;
608
695
  declare function fileRoutes(app: FastifyInstance, opts: {
609
696
  workspace?: Workspace;
610
697
  getWorkspace?: (request: FastifyRequest) => Workspace | Promise<Workspace>;
698
+ filesystemBindings?: RuntimeFilesystemBinding[];
699
+ getFilesystemBindings?: (request: FastifyRequest) => RuntimeFilesystemBinding[] | undefined | Promise<RuntimeFilesystemBinding[] | undefined>;
611
700
  }, done: (err?: Error) => void): void;
612
701
 
613
702
  interface RuntimeTemplateContribution {
@@ -964,6 +1053,7 @@ interface RegisterAgentRoutesOptions {
964
1053
  workspaceRoot: string;
965
1054
  runtimeMode: RuntimeModeId;
966
1055
  workspaceFsCapability?: Workspace['fsCapability'];
1056
+ authSubject?: string;
967
1057
  }) => AgentTool[] | Promise<AgentTool[]>;
968
1058
  systemPromptAppend?: string;
969
1059
  /** Optional dynamic system-prompt source forwarded to the harness. */
@@ -13,7 +13,7 @@ import {
13
13
  StopPayloadSchema,
14
14
  extractToolUiMetadata,
15
15
  sanitizeToolUiMetadata2 as sanitizeToolUiMetadata
16
- } from "../chunk-G6YPXDHR.js";
16
+ } from "../chunk-UPRDF2PB.js";
17
17
 
18
18
  // src/server/sandbox/direct/createDirectSandbox.ts
19
19
  import { spawn } from "child_process";
@@ -2656,6 +2656,9 @@ var BORING_SETTINGS_PATH = ".boring/settings";
2656
2656
  var DEFAULT_MARKDOWN_IMAGE_UPLOAD_DIR = "assets/images";
2657
2657
  var DEFAULT_FILE_UPLOAD_DIR = "assets/uploads";
2658
2658
  var MAX_UPLOAD_BYTES = 10 * 1024 * 1024;
2659
+ var USER_FILESYSTEM_ID = "user";
2660
+ var ERROR_CODE_NOT_FOUND_OR_DENIED = "not_found_or_denied";
2661
+ var ERROR_CODE_READONLY = "readonly";
2659
2662
  var IMAGE_UPLOAD_EXTENSIONS = /* @__PURE__ */ new Set(["avif", "gif", "jpg", "jpeg", "png", "webp"]);
2660
2663
  var SAFE_UNKNOWN_FILE_EXTENSIONS = /* @__PURE__ */ new Set(["csv", "doc", "docx", "json", "md", "pdf", "ppt", "pptx", "rtf", "txt", "xls", "xlsx", "zip"]);
2661
2664
  var MIME_UPLOAD_EXTENSIONS = {
@@ -2723,6 +2726,14 @@ function classifyError(err, reply, subject) {
2723
2726
  error: { code: ERROR_CODE_INTERNAL, message }
2724
2727
  });
2725
2728
  }
2729
+ function requestedFilesystem(value) {
2730
+ return typeof value === "string" && value.length > 0 ? value : USER_FILESYSTEM_ID;
2731
+ }
2732
+ function sendNotFoundOrDenied(reply) {
2733
+ return reply.code(404).send({
2734
+ error: { code: ERROR_CODE_NOT_FOUND_OR_DENIED, message: "not found or denied" }
2735
+ });
2736
+ }
2726
2737
  function requireStringParam(value, field, reply) {
2727
2738
  if (typeof value !== "string" || value.length === 0) {
2728
2739
  reply.code(400).send({
@@ -2842,16 +2853,41 @@ function sendValidationError(reply, message, field) {
2842
2853
  error: { code: ERROR_CODE_VALIDATION_ERROR, message, ...field ? { field } : {} }
2843
2854
  });
2844
2855
  }
2856
+ function sendFilesystemBindingMutationDenied(reply, filesystem, access5) {
2857
+ return reply.code(403).send({
2858
+ error: { code: ERROR_CODE_READONLY, message: `${filesystem} binding is ${access5}` }
2859
+ });
2860
+ }
2845
2861
  function fileRoutes(app, opts, done) {
2846
2862
  async function resolveWorkspace(request) {
2847
2863
  if (opts.getWorkspace) return await opts.getWorkspace(request);
2848
2864
  if (opts.workspace) return opts.workspace;
2849
2865
  throw new Error("file route requires workspace or getWorkspace");
2850
2866
  }
2867
+ async function resolveFilesystemBindings(request) {
2868
+ if (opts.getFilesystemBindings) return await opts.getFilesystemBindings(request) ?? [];
2869
+ if (opts.filesystemBindings) return opts.filesystemBindings;
2870
+ return [];
2871
+ }
2872
+ async function resolveFilesystemBinding(request, filesystem) {
2873
+ return (await resolveFilesystemBindings(request)).find((binding) => binding.filesystem === filesystem);
2874
+ }
2851
2875
  app.get("/api/v1/files/raw", async (request, reply) => {
2852
2876
  const query = request.query;
2853
2877
  const path4 = requireStringParam(query.path, "path", reply);
2854
2878
  if (path4 === null) return;
2879
+ const filesystem = requestedFilesystem(query.filesystem);
2880
+ if (filesystem !== USER_FILESYSTEM_ID) {
2881
+ try {
2882
+ const binding = await resolveFilesystemBinding(request, filesystem);
2883
+ if (!binding) return sendNotFoundOrDenied(reply);
2884
+ const result = await binding.operations.read({ filesystem, path: path4 });
2885
+ const bytes = Buffer.from(result.content, "utf8");
2886
+ return reply.header("content-type", contentTypeForPath(path4)).header("content-length", String(bytes.byteLength)).header("cache-control", "no-store").header("x-content-type-options", "nosniff").send(bytes);
2887
+ } catch {
2888
+ return sendNotFoundOrDenied(reply);
2889
+ }
2890
+ }
2855
2891
  try {
2856
2892
  const workspace = await resolveWorkspace(request);
2857
2893
  if (!workspace.readBinaryFile) {
@@ -2905,6 +2941,17 @@ function fileRoutes(app, opts, done) {
2905
2941
  const query = request.query;
2906
2942
  const path4 = requireStringParam(query.path, "path", reply);
2907
2943
  if (path4 === null) return;
2944
+ const filesystem = requestedFilesystem(query.filesystem);
2945
+ if (filesystem !== USER_FILESYSTEM_ID) {
2946
+ try {
2947
+ const binding = await resolveFilesystemBinding(request, filesystem);
2948
+ if (!binding) return sendNotFoundOrDenied(reply);
2949
+ const result = await binding.operations.read({ filesystem, path: path4 });
2950
+ return { content: result.content, access: binding.access };
2951
+ } catch {
2952
+ return sendNotFoundOrDenied(reply);
2953
+ }
2954
+ }
2908
2955
  try {
2909
2956
  if (isReadonlySkillFilePath(path4)) {
2910
2957
  const { content: content2, stat: stat13 } = await readReadonlySkillFile(path4);
@@ -2918,11 +2965,11 @@ function fileRoutes(app, opts, done) {
2918
2965
  const workspace = await resolveWorkspace(request);
2919
2966
  if (workspace.readFileWithStat) {
2920
2967
  const { content: content2, stat: stat13 } = await workspace.readFileWithStat(path4);
2921
- return { content: content2, mtimeMs: stat13.kind === "file" ? stat13.mtimeMs : void 0 };
2968
+ return { content: content2, mtimeMs: stat13.kind === "file" ? stat13.mtimeMs : void 0, access: "readwrite" };
2922
2969
  }
2923
2970
  const content = await workspace.readFile(path4);
2924
2971
  const stat12 = await workspace.stat(path4);
2925
- return { content, mtimeMs: stat12.kind === "file" ? stat12.mtimeMs : void 0 };
2972
+ return { content, mtimeMs: stat12.kind === "file" ? stat12.mtimeMs : void 0, access: "readwrite" };
2926
2973
  } catch (err) {
2927
2974
  return classifyError(err, reply, "file");
2928
2975
  }
@@ -2936,8 +2983,28 @@ function fileRoutes(app, opts, done) {
2936
2983
  error: { code: ERROR_CODE_VALIDATION_ERROR, message: "content is required", field: "content" }
2937
2984
  });
2938
2985
  }
2986
+ const filesystem = requestedFilesystem(body.filesystem);
2939
2987
  const expectedMtimeMs = typeof body.expectedMtimeMs === "number" ? body.expectedMtimeMs : null;
2940
2988
  const shouldReturnMtimeMs = body.returnMtimeMs !== false || expectedMtimeMs !== null;
2989
+ if (filesystem !== USER_FILESYSTEM_ID) {
2990
+ try {
2991
+ const binding = await resolveFilesystemBinding(request, filesystem);
2992
+ if (!binding) return sendNotFoundOrDenied(reply);
2993
+ if (binding.access !== "readwrite") return sendFilesystemBindingMutationDenied(reply, filesystem, binding.access);
2994
+ if (!binding.operations.write) {
2995
+ return reply.code(501).send({ error: { code: ERROR_CODE_INTERNAL, message: `${filesystem} write operation is unavailable` } });
2996
+ }
2997
+ const result = await binding.operations.write({
2998
+ filesystem,
2999
+ path: path4,
3000
+ content: body.content,
3001
+ ...expectedMtimeMs !== null ? { expectedMtimeMs } : {}
3002
+ });
3003
+ return shouldReturnMtimeMs ? { ok: true, mtimeMs: result.mtimeMs } : { ok: true };
3004
+ } catch (err) {
3005
+ return classifyError(err, reply, "file");
3006
+ }
3007
+ }
2941
3008
  try {
2942
3009
  const workspace = await resolveWorkspace(request);
2943
3010
  if (expectedMtimeMs !== null) {
@@ -3069,6 +3136,21 @@ function fileRoutes(app, opts, done) {
3069
3136
  const query = request.query;
3070
3137
  const path4 = requireStringParam(query.path, "path", reply);
3071
3138
  if (path4 === null) return;
3139
+ const filesystem = requestedFilesystem(query.filesystem);
3140
+ if (filesystem !== USER_FILESYSTEM_ID) {
3141
+ try {
3142
+ const binding = await resolveFilesystemBinding(request, filesystem);
3143
+ if (!binding) return sendNotFoundOrDenied(reply);
3144
+ if (binding.access !== "readwrite") return sendFilesystemBindingMutationDenied(reply, filesystem, binding.access);
3145
+ if (!binding.operations.delete) {
3146
+ return reply.code(501).send({ error: { code: ERROR_CODE_INTERNAL, message: `${filesystem} delete operation is unavailable` } });
3147
+ }
3148
+ await binding.operations.delete({ filesystem, path: path4 });
3149
+ return { ok: true };
3150
+ } catch (err) {
3151
+ return classifyError(err, reply, "file");
3152
+ }
3153
+ }
3072
3154
  try {
3073
3155
  const workspace = await resolveWorkspace(request);
3074
3156
  await workspace.unlink(path4);
@@ -3083,6 +3165,21 @@ function fileRoutes(app, opts, done) {
3083
3165
  if (from === null) return;
3084
3166
  const to = requireStringParam(body?.to, "to", reply);
3085
3167
  if (to === null) return;
3168
+ const filesystem = requestedFilesystem(body.filesystem);
3169
+ if (filesystem !== USER_FILESYSTEM_ID) {
3170
+ try {
3171
+ const binding = await resolveFilesystemBinding(request, filesystem);
3172
+ if (!binding) return sendNotFoundOrDenied(reply);
3173
+ if (binding.access !== "readwrite") return sendFilesystemBindingMutationDenied(reply, filesystem, binding.access);
3174
+ if (!binding.operations.move) {
3175
+ return reply.code(501).send({ error: { code: ERROR_CODE_INTERNAL, message: `${filesystem} move operation is unavailable` } });
3176
+ }
3177
+ await binding.operations.move({ filesystem, from, to });
3178
+ return { ok: true };
3179
+ } catch (err) {
3180
+ return classifyError(err, reply, "file");
3181
+ }
3182
+ }
3086
3183
  try {
3087
3184
  const workspace = await resolveWorkspace(request);
3088
3185
  await workspace.rename(from, to);
@@ -3096,6 +3193,21 @@ function fileRoutes(app, opts, done) {
3096
3193
  const path4 = requireStringParam(body?.path, "path", reply);
3097
3194
  if (path4 === null) return;
3098
3195
  const recursive = body.recursive === true;
3196
+ const filesystem = requestedFilesystem(body.filesystem);
3197
+ if (filesystem !== USER_FILESYSTEM_ID) {
3198
+ try {
3199
+ const binding = await resolveFilesystemBinding(request, filesystem);
3200
+ if (!binding) return sendNotFoundOrDenied(reply);
3201
+ if (binding.access !== "readwrite") return sendFilesystemBindingMutationDenied(reply, filesystem, binding.access);
3202
+ if (!binding.operations.mkdir) {
3203
+ return reply.code(501).send({ error: { code: ERROR_CODE_INTERNAL, message: `${filesystem} mkdir operation is unavailable` } });
3204
+ }
3205
+ await binding.operations.mkdir({ filesystem, path: path4, recursive });
3206
+ return { ok: true };
3207
+ } catch (err) {
3208
+ return classifyError(err, reply, "directory");
3209
+ }
3210
+ }
3099
3211
  try {
3100
3212
  const workspace = await resolveWorkspace(request);
3101
3213
  await workspace.mkdir(path4, { recursive });
@@ -3108,6 +3220,17 @@ function fileRoutes(app, opts, done) {
3108
3220
  const query = request.query;
3109
3221
  const path4 = requireStringParam(query.path, "path", reply);
3110
3222
  if (path4 === null) return;
3223
+ const filesystem = requestedFilesystem(query.filesystem);
3224
+ if (filesystem !== USER_FILESYSTEM_ID) {
3225
+ try {
3226
+ const binding = await resolveFilesystemBinding(request, filesystem);
3227
+ if (!binding) return sendNotFoundOrDenied(reply);
3228
+ const result = await binding.operations.stat({ filesystem, path: path4 });
3229
+ return result.isDirectory ? { kind: "dir" } : { kind: "file", size: 0 };
3230
+ } catch {
3231
+ return sendNotFoundOrDenied(reply);
3232
+ }
3233
+ }
3111
3234
  try {
3112
3235
  if (isReadonlySkillFilePath(path4)) {
3113
3236
  return await statReadonlySkillFile(path4);
@@ -8327,17 +8450,135 @@ function buildRemoteWorkspaceFilesystemAgentTools(bundle, pathOptions) {
8327
8450
  function isTextContent2(content) {
8328
8451
  return content.type === "text" && typeof content.text === "string";
8329
8452
  }
8453
+ function filesystemBindings(bundle) {
8454
+ return bundle.filesystemBindings ?? [];
8455
+ }
8456
+ function filesystemIds(bundle) {
8457
+ return filesystemBindings(bundle).map((binding) => binding.filesystem);
8458
+ }
8459
+ function withFilesystemParameter(parameters, filesystemIds2) {
8460
+ const schema = parameters && typeof parameters === "object" ? { ...parameters } : { type: "object" };
8461
+ const properties = schema.properties && typeof schema.properties === "object" ? { ...schema.properties } : {};
8462
+ return {
8463
+ ...schema,
8464
+ properties: {
8465
+ ...properties,
8466
+ filesystem: {
8467
+ type: "string",
8468
+ description: filesystemIds2.length > 0 ? "Logical filesystem to use. Omit or use user for workspace files; use an advertised named filesystem for bound readonly context." : "Logical filesystem to use. Omit or use user for workspace files.",
8469
+ enum: ["user", ...filesystemIds2]
8470
+ }
8471
+ }
8472
+ };
8473
+ }
8474
+ function requestedFilesystem2(params) {
8475
+ const value = params.filesystem;
8476
+ return typeof value === "string" && value.length > 0 ? value : "user";
8477
+ }
8478
+ function withoutFilesystem(params) {
8479
+ const { filesystem: _filesystem, ...rest } = params;
8480
+ return rest;
8481
+ }
8482
+ function boundFilesystemPath(params) {
8483
+ const value = params.path;
8484
+ return typeof value === "string" && value.length > 0 ? value : "/";
8485
+ }
8486
+ function assertNotFilesystemPathSpoof(path4, filesystemIds2) {
8487
+ const normalized = path4.replace(/\\/g, "/");
8488
+ if (normalized.includes(":/") || filesystemIds2.some((filesystem) => normalized === `/${filesystem}` || normalized.startsWith(`/${filesystem}/`))) {
8489
+ throw new Error("filesystem prefixes are not valid path strings; use the filesystem parameter");
8490
+ }
8491
+ }
8492
+ function filesystemBinding(bundle, filesystem) {
8493
+ return filesystemBindings(bundle).find((binding) => binding.filesystem === filesystem);
8494
+ }
8495
+ function boundOperations(bundle, filesystem) {
8496
+ const binding = filesystemBinding(bundle, filesystem);
8497
+ if (!binding) throw new Error(`No filesystem binding is available for ${filesystem}`);
8498
+ return binding.operations;
8499
+ }
8500
+ function formatBoundRead(content) {
8501
+ return { content: [{ type: "text", text: content }] };
8502
+ }
8503
+ function formatBoundList(entries) {
8504
+ return { content: [{ type: "text", text: entries.join("\n") }] };
8505
+ }
8506
+ function formatBoundFind(paths) {
8507
+ return { content: [{ type: "text", text: paths.join("\n") }] };
8508
+ }
8509
+ function formatBoundGrep(matches) {
8510
+ return { content: [{ type: "text", text: matches.map((match) => `${match.path}:${match.line}:${match.text}`).join("\n") }] };
8511
+ }
8512
+ async function executeBoundFilesystemTool(toolName, filesystem, params, bundle) {
8513
+ const path4 = boundFilesystemPath(params);
8514
+ assertNotFilesystemPathSpoof(path4, filesystemIds(bundle));
8515
+ const operations = boundOperations(bundle, filesystem);
8516
+ if (toolName === "read") {
8517
+ const result = await operations.read({ filesystem, path: path4 });
8518
+ return { ...formatBoundRead(result.content), details: { metadata: result.metadata } };
8519
+ }
8520
+ if (toolName === "ls") {
8521
+ const result = await operations.list({ filesystem, path: path4 });
8522
+ return { ...formatBoundList(result.entries), details: { metadata: result.metadata } };
8523
+ }
8524
+ if (toolName === "find") {
8525
+ const pattern = typeof params.pattern === "string" ? params.pattern : "*";
8526
+ const limit = typeof params.limit === "number" ? params.limit : void 0;
8527
+ const result = await operations.find({ filesystem, path: path4 }, pattern, { limit });
8528
+ return { ...formatBoundFind(result.paths), details: { metadata: result.metadata } };
8529
+ }
8530
+ if (toolName === "grep") {
8531
+ const pattern = typeof params.pattern === "string" ? params.pattern : "";
8532
+ const limit = typeof params.limit === "number" ? params.limit : void 0;
8533
+ const result = await operations.grep({ filesystem, path: path4 }, pattern, { limit });
8534
+ return { ...formatBoundGrep(result.matches), details: { metadata: result.metadata } };
8535
+ }
8536
+ if (toolName === "write" || toolName === "edit") {
8537
+ operations.rejectMutation(toolName, { filesystem, path: path4 });
8538
+ }
8539
+ throw new Error(`Tool ${toolName} does not support filesystem ${filesystem}`);
8540
+ }
8541
+ function withBoundFilesystemPromptGuidance(promptSnippet, filesystemIds2) {
8542
+ if (filesystemIds2.length === 0) return promptSnippet;
8543
+ const guidance = [
8544
+ "Named filesystem bindings: file tools default to the user workspace when filesystem is omitted.",
8545
+ `Use the filesystem parameter explicitly for bound readonly context (${filesystemIds2.join(", ")}), and start browsing at / unless told otherwise.`,
8546
+ "Readonly filesystem bindings reject writes; do not use path prefixes like filesystem:/x to switch filesystem."
8547
+ ].join("\n");
8548
+ return [promptSnippet, guidance].filter(Boolean).join("\n");
8549
+ }
8550
+ function withFilesystemRouting(tool, bundle) {
8551
+ const ids = filesystemIds(bundle);
8552
+ return {
8553
+ ...tool,
8554
+ promptSnippet: withBoundFilesystemPromptGuidance(tool.promptSnippet, ids),
8555
+ parameters: withFilesystemParameter(tool.parameters, ids),
8556
+ async execute(params, ctx) {
8557
+ const filesystem = requestedFilesystem2(params);
8558
+ if (filesystem !== "user") {
8559
+ const result = await executeBoundFilesystemTool(tool.name, filesystem, params, bundle);
8560
+ const textContent = (result.content ?? []).filter(isTextContent2).map((c) => ({ type: "text", text: c.text }));
8561
+ return {
8562
+ content: textContent.length > 0 ? textContent : [{ type: "text", text: "" }],
8563
+ isError: false,
8564
+ details: result.details
8565
+ };
8566
+ }
8567
+ return await tool.execute(withoutFilesystem(params), ctx);
8568
+ }
8569
+ };
8570
+ }
8330
8571
  function adaptPiTool2(piTool) {
8331
8572
  return {
8332
8573
  name: piTool.name,
8333
8574
  readinessRequirements: ["workspace-fs"],
8334
8575
  description: piTool.description,
8335
8576
  promptSnippet: piTool.promptSnippet,
8336
- parameters: piTool.parameters,
8577
+ parameters: piTool.parameters && typeof piTool.parameters === "object" ? { ...piTool.parameters } : { type: "object" },
8337
8578
  async execute(params, ctx) {
8338
8579
  const result = await piTool.execute(
8339
8580
  ctx.toolCallId,
8340
- params,
8581
+ withoutFilesystem(params),
8341
8582
  ctx.abortSignal,
8342
8583
  ctx.onUpdate ? (update) => {
8343
8584
  const text = update.content?.filter(isTextContent2).map((c) => c.text).join("");
@@ -8361,7 +8602,7 @@ function buildFilesystemAgentTools(bundle) {
8361
8602
  const cwd = bundle.workspace.root;
8362
8603
  const strategy = bundle.filesystem ?? defaultFilesystemStrategyForBundle(bundle);
8363
8604
  if (strategy.kind === "remote-workspace") {
8364
- return buildRemoteWorkspaceFilesystemAgentTools(bundle, strategy.pathOptions);
8605
+ return buildRemoteWorkspaceFilesystemAgentTools(bundle, strategy.pathOptions).map((tool) => withFilesystemRouting(tool, bundle));
8365
8606
  }
8366
8607
  const storageRoot = getRuntimeBundleStorageRoot(bundle);
8367
8608
  const ops = boundFs(storageRoot, { runtimeRoot: cwd });
@@ -8372,7 +8613,7 @@ function buildFilesystemAgentTools(bundle) {
8372
8613
  adaptPiTool2(createFindToolDefinition2(cwd, { operations: ops.find })),
8373
8614
  adaptPiTool2(createGrepToolDefinition2(cwd, { operations: ops.grep })),
8374
8615
  adaptPiTool2(createLsToolDefinition2(cwd, { operations: ops.ls }))
8375
- ];
8616
+ ].map((tool) => withFilesystemRouting(tool, bundle));
8376
8617
  }
8377
8618
 
8378
8619
  // src/server/tools/harness/index.ts
@@ -9009,6 +9250,14 @@ function joinPath2(base, name) {
9009
9250
  if (base === ".") return name;
9010
9251
  return `${base}/${name}`;
9011
9252
  }
9253
+ function requestedFilesystem3(value) {
9254
+ return typeof value === "string" && value.length > 0 ? value : "user";
9255
+ }
9256
+ function sendNotFoundOrDenied2(reply) {
9257
+ return reply.code(404).send({
9258
+ error: { code: ERROR_CODE_NOT_FOUND_OR_DENIED, message: "not found or denied" }
9259
+ });
9260
+ }
9012
9261
  async function listTree(workspace, dir, recursive) {
9013
9262
  const entries = [];
9014
9263
  const initialEntries = await workspace.readdir(dir);
@@ -9033,12 +9282,44 @@ async function listTree(workspace, dir, recursive) {
9033
9282
  }
9034
9283
  return entries;
9035
9284
  }
9285
+ async function listBoundTree(binding, dir, recursive) {
9286
+ const root = dir === "." ? "/" : dir;
9287
+ const displayParentDir = dir === "." || dir === "/" ? "." : dir.replace(/\/+$/, "");
9288
+ const entries = [];
9289
+ const rootEntries = await binding.operations.list({ filesystem: binding.filesystem, path: root });
9290
+ const queue = [
9291
+ { parentDir: displayParentDir || ".", items: rootEntries.entries, depth: 0 }
9292
+ ];
9293
+ while (queue.length > 0) {
9294
+ if (entries.length >= MAX_ENTRIES) break;
9295
+ const batch = queue.shift();
9296
+ for (const name of batch.items) {
9297
+ if (entries.length >= MAX_ENTRIES) break;
9298
+ const entryPath = joinPath2(batch.parentDir, name);
9299
+ const stat12 = await binding.operations.stat({ filesystem: binding.filesystem, path: entryPath });
9300
+ const kind = stat12.isDirectory ? "dir" : "file";
9301
+ entries.push({ name, kind, path: entryPath });
9302
+ if (recursive && kind === "dir" && batch.depth < MAX_DEPTH && !isIgnoredDirName(name)) {
9303
+ try {
9304
+ const childEntries = await binding.operations.list({ filesystem: binding.filesystem, path: entryPath });
9305
+ queue.push({ parentDir: entryPath, items: childEntries.entries, depth: batch.depth + 1 });
9306
+ } catch {
9307
+ }
9308
+ }
9309
+ }
9310
+ }
9311
+ return entries;
9312
+ }
9036
9313
  function treeRoutes(app, opts, done) {
9037
9314
  async function resolveWorkspace(request) {
9038
9315
  if (opts.getWorkspace) return await opts.getWorkspace(request);
9039
9316
  if (opts.workspace) return opts.workspace;
9040
9317
  throw new Error("workspace route requires workspace or getWorkspace");
9041
9318
  }
9319
+ async function resolveFilesystemBinding(request, filesystem) {
9320
+ const bindings = opts.getFilesystemBindings ? await opts.getFilesystemBindings(request) ?? [] : opts.filesystemBindings ?? [];
9321
+ return bindings.find((binding) => binding.filesystem === filesystem);
9322
+ }
9042
9323
  app.get(
9043
9324
  "/api/v1/tree",
9044
9325
  async (request, reply) => {
@@ -9054,6 +9335,16 @@ function treeRoutes(app, opts, done) {
9054
9335
  throw err;
9055
9336
  }
9056
9337
  const recursive = request.query.recursive === "true";
9338
+ const filesystem = requestedFilesystem3(request.query.filesystem);
9339
+ if (filesystem !== "user") {
9340
+ try {
9341
+ const binding = await resolveFilesystemBinding(request, filesystem);
9342
+ if (!binding) return sendNotFoundOrDenied2(reply);
9343
+ return { entries: await listBoundTree(binding, dir, recursive) };
9344
+ } catch {
9345
+ return sendNotFoundOrDenied2(reply);
9346
+ }
9347
+ }
9057
9348
  try {
9058
9349
  const workspace = await resolveWorkspace(request);
9059
9350
  const entries = await listTree(workspace, dir, recursive);
@@ -12321,7 +12612,7 @@ async function createAgentApp(opts = {}) {
12321
12612
  });
12322
12613
  await app.register(fileRoutes, { workspace: runtimeBundle.workspace });
12323
12614
  await app.register(fsEventsRoutes, { workspace: runtimeBundle.workspace });
12324
- await app.register(treeRoutes, { workspace: runtimeBundle.workspace });
12615
+ await app.register(treeRoutes, { workspace: runtimeBundle.workspace, filesystemBindings: runtimeBundle.filesystemBindings });
12325
12616
  await app.register(searchRoutes, { fileSearch: runtimeBundle.fileSearch });
12326
12617
  await app.register(gitRoutes, {
12327
12618
  getWorkspaceRoot: () => getOptionalRuntimeBundleStorageRoot(runtimeBundle)
@@ -12579,6 +12870,12 @@ function normalizeSessionNamespace(value) {
12579
12870
  const trimmed = value.trim();
12580
12871
  return trimmed.length > 0 ? trimmed : void 0;
12581
12872
  }
12873
+ function getRequestAuthSubject(request) {
12874
+ const userId = request?.user?.id;
12875
+ if (typeof userId === "string" && userId.trim()) return userId.trim();
12876
+ const authSubject = request?.workspaceContext?.authSubject;
12877
+ return typeof authSubject === "string" && authSubject.trim() ? authSubject.trim() : void 0;
12878
+ }
12582
12879
  function createHttpError(code, message, details = {}) {
12583
12880
  const error = new Error(message);
12584
12881
  error.code = code;
@@ -12637,7 +12934,7 @@ var registerAgentRoutes = async (app, opts) => {
12637
12934
  app.addHook("onClose", async () => {
12638
12935
  await modeAdapter.dispose?.();
12639
12936
  });
12640
- const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getPi === "function" || typeof opts.getSessionNamespace === "function" || typeof opts.getSystemPromptDynamic === "function";
12937
+ const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getPi === "function" || typeof opts.getExtraTools === "function" || typeof opts.getSessionNamespace === "function" || typeof opts.getSystemPromptDynamic === "function";
12641
12938
  const sessionChangesTracker = new InMemorySessionChangesTracker();
12642
12939
  const externalPluginsEnabled = opts.externalPlugins !== false;
12643
12940
  const runtimeBindings = /* @__PURE__ */ new Map();
@@ -12657,6 +12954,7 @@ var registerAgentRoutes = async (app, opts) => {
12657
12954
  const scopedTemplatePath = opts.getTemplatePath ? await opts.getTemplatePath({ workspaceId, workspaceRoot: root, request }) : templatePath;
12658
12955
  const pi = await resolveScopePi(workspaceId, root, request);
12659
12956
  const sessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot: root, request }) : opts.sessionNamespace);
12957
+ const extraToolsAuthSubject = opts.getExtraTools ? getRequestAuthSubject(request) : void 0;
12660
12958
  return {
12661
12959
  root,
12662
12960
  templatePath: scopedTemplatePath,
@@ -12668,7 +12966,8 @@ var registerAgentRoutes = async (app, opts) => {
12668
12966
  root,
12669
12967
  scopedTemplatePath ?? null,
12670
12968
  pi,
12671
- sessionNamespace ?? null
12969
+ sessionNamespace ?? null,
12970
+ extraToolsAuthSubject ?? null
12672
12971
  ])
12673
12972
  };
12674
12973
  }
@@ -12852,7 +13151,8 @@ var registerAgentRoutes = async (app, opts) => {
12852
13151
  workspaceId,
12853
13152
  workspaceRoot: root,
12854
13153
  runtimeMode: resolvedMode,
12855
- workspaceFsCapability: runtimeBundle.workspace.fsCapability
13154
+ workspaceFsCapability: runtimeBundle.workspace.fsCapability,
13155
+ authSubject: getRequestAuthSubject(request)
12856
13156
  }) : [];
12857
13157
  const tools = mergeTools({
12858
13158
  standardTools,
@@ -12944,7 +13244,8 @@ var registerAgentRoutes = async (app, opts) => {
12944
13244
  return await ensureRuntimeBindingReady(
12945
13245
  workspaceId,
12946
13246
  scope,
12947
- await existing.promise
13247
+ await existing.promise,
13248
+ request
12948
13249
  );
12949
13250
  }
12950
13251
  }
@@ -12958,17 +13259,18 @@ var registerAgentRoutes = async (app, opts) => {
12958
13259
  return await ensureRuntimeBindingReady(
12959
13260
  workspaceId,
12960
13261
  scope,
12961
- await created.promise
13262
+ await created.promise,
13263
+ request
12962
13264
  );
12963
13265
  } catch (error) {
12964
13266
  if (runtimeBindings.get(scope.key) === created) runtimeBindings.delete(scope.key);
12965
13267
  throw error;
12966
13268
  }
12967
13269
  }
12968
- async function recreateRuntimeBinding(workspaceId, scope) {
13270
+ async function recreateRuntimeBinding(workspaceId, scope, request) {
12969
13271
  runtimeBindings.delete(scope.key);
12970
13272
  modeAdapter.evictCachedRuntime?.({ workspaceId });
12971
- const created = createRuntimeBindingEntry(workspaceId, scope);
13273
+ const created = createRuntimeBindingEntry(workspaceId, scope, request);
12972
13274
  runtimeBindings.set(scope.key, created);
12973
13275
  evictRuntimeBindings();
12974
13276
  try {
@@ -12980,7 +13282,7 @@ var registerAgentRoutes = async (app, opts) => {
12980
13282
  throw error;
12981
13283
  }
12982
13284
  }
12983
- async function ensureRuntimeBindingReady(workspaceId, scope, binding) {
13285
+ async function ensureRuntimeBindingReady(workspaceId, scope, binding, request) {
12984
13286
  const healthCheck = modeAdapter.cachedBindingHealthCheck;
12985
13287
  if (!healthCheck) return binding;
12986
13288
  const now = Date.now();
@@ -12997,7 +13299,7 @@ var registerAgentRoutes = async (app, opts) => {
12997
13299
  err: result.error,
12998
13300
  workspaceId
12999
13301
  }, result.message ?? "[runtime] cached runtime invalid; recreating");
13000
- return await recreateRuntimeBinding(workspaceId, scope);
13302
+ return await recreateRuntimeBinding(workspaceId, scope, request);
13001
13303
  }
13002
13304
  const hasRuntimeProvisioningInput = opts.provisionWorkspace !== false && Boolean(opts.provisionRuntime);
13003
13305
  const staticBinding = requestScopedRuntime ? null : await getOrCreateRuntimeBinding(sessionId);
@@ -13090,7 +13392,8 @@ var registerAgentRoutes = async (app, opts) => {
13090
13392
  getWorkspace: async (request) => (await getBindingForRequest(request)).runtimeBundle.workspace
13091
13393
  });
13092
13394
  await app.register(treeRoutes, {
13093
- getWorkspace: async (request) => (await getBindingForRequest(request)).runtimeBundle.workspace
13395
+ getWorkspace: async (request) => (await getBindingForRequest(request)).runtimeBundle.workspace,
13396
+ getFilesystemBindings: async (request) => (await getBindingForRequest(request)).runtimeBundle.filesystemBindings
13094
13397
  });
13095
13398
  await app.register(searchRoutes, {
13096
13399
  getFileSearch: async (request) => (await getBindingForRequest(request)).runtimeBundle.fileSearch
@@ -1,7 +1,7 @@
1
1
  import { W as Workspace, S as Sandbox, F as FileSearch, A as AgentTool } from '../agentPluginEvents-Ddn5DQ5E.js';
2
2
  export { a as AgentHarness, C as CommandNotifyPayload, E as Entry, b as ExecOptions, c as ExecResult, I as IsolatedCodeInput, d as IsolatedCodeOutput, J as JSONSchema, R as RunContext, e as SandboxCapability, f as SandboxHandleRecord, g as SandboxHandleStore, h as SendMessageInput, i as Stat, T as TelemetryEvent, j as TelemetrySink, k as ToolExecContext, l as ToolResult, m as WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT, n as WORKSPACE_COMMAND_NOTIFY_EVENT, o as WorkspaceRuntimeContext, p as noopTelemetry, s as safeCapture } from '../agentPluginEvents-Ddn5DQ5E.js';
3
- import { B as BoringChatPart, T as ToolUiMetadata } from '../piChatEvent-B6GDifo2.js';
4
- export { A as ApiErrorPayload, a as ApiErrorPayloadSchema, b as ApiErrorResponse, c as ApiErrorResponseSchema, d as BoringChatMessage, C as ChatError, e as CommandReceipt, E as ERROR_CODES, f as ErrorCode, g as ErrorLogFields, h as ErrorLogFieldsSchema, F as FollowUpPayload, i as FollowUpReceipt, I as InterruptPayload, j as InterruptReceipt, P as PiChatEvent, k as PiChatHeartbeatFrame, l as PiChatSnapshot, m as PiChatStatus, n as PiChatStreamFrame, o as PromptPayload, p as PromptReceipt, Q as QueueClearPayload, q as QueueClearReceipt, r as QueuedUserMessage, S as StopPayload, s as StopReceipt, t as extractToolUiMetadata, u as isToolUiMetadata } from '../piChatEvent-B6GDifo2.js';
3
+ import { B as BoringChatPart, T as ToolUiMetadata } from '../piChatEvent-JbwYVBZj.js';
4
+ export { A as ApiErrorPayload, a as ApiErrorPayloadSchema, b as ApiErrorResponse, c as ApiErrorResponseSchema, d as BoringChatMessage, C as ChatError, e as CommandReceipt, E as ERROR_CODES, f as ErrorCode, g as ErrorLogFields, h as ErrorLogFieldsSchema, F as FollowUpPayload, i as FollowUpReceipt, I as InterruptPayload, j as InterruptReceipt, P as PiChatEvent, k as PiChatHeartbeatFrame, l as PiChatSnapshot, m as PiChatStatus, n as PiChatStreamFrame, o as PromptPayload, p as PromptReceipt, Q as QueueClearPayload, q as QueueClearReceipt, r as QueuedUserMessage, S as StopPayload, s as StopReceipt, t as extractToolUiMetadata, u as isToolUiMetadata } from '../piChatEvent-JbwYVBZj.js';
5
5
  export { C as ChatAttachmentPayload, a as ChatModelSelection, b as ChatSubmitPayload, S as SessionCtx, c as SessionDetail, d as SessionStore, e as SessionSummary, T as ThinkingLevel } from '../chatSubmitPayload-DHqQL2wD.js';
6
6
  import { z } from 'zod';
7
7
 
@@ -39,7 +39,7 @@ import {
39
39
  extractToolUiMetadata,
40
40
  isToolUiMetadata,
41
41
  sanitizeToolUiMetadata2 as sanitizeToolUiMetadata
42
- } from "../chunk-G6YPXDHR.js";
42
+ } from "../chunk-UPRDF2PB.js";
43
43
 
44
44
  // src/shared/config-schema.ts
45
45
  import { z } from "zod";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-agent",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
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.62"
77
+ "@hachej/boring-ui-kit": "0.1.64"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@antithesishq/bombadil": "0.5.0",