pi-fabric 0.92.32 → 0.92.33

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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-J2HV2URQ.js";
5
5
  import {
6
6
  NESTED_TOOL_CALL_ID_PREFIX
7
- } from "./chunk-JOEFBOFN.js";
7
+ } from "./chunk-DLLIRB63.js";
8
8
  import {
9
9
  THINKING_DIGEST_CUSTOM_TYPE,
10
10
  buildThinkingDigest,
@@ -2012,4 +2012,4 @@ export {
2012
2012
  PrewalkController,
2013
2013
  PrewalkDriftTracker
2014
2014
  };
2015
- //# sourceMappingURL=chunk-DUMIK5RZ.js.map
2015
+ //# sourceMappingURL=chunk-CEXO6ELQ.js.map
@@ -26,7 +26,7 @@ import {
26
26
  } from "./chunk-PSNXUZMY.js";
27
27
  import {
28
28
  FABRIC_NESTED_TOOL_CALL_ID_PREFIX
29
- } from "./chunk-FAMBJFFF.js";
29
+ } from "./chunk-IJL3QK3J.js";
30
30
  import {
31
31
  FabricResolutionError,
32
32
  FabricTraceSafeError,
@@ -1467,4 +1467,4 @@ export {
1467
1467
  NESTED_TOOL_CALL_ID_PREFIX,
1468
1468
  ActionRegistry
1469
1469
  };
1470
- //# sourceMappingURL=chunk-JOEFBOFN.js.map
1470
+ //# sourceMappingURL=chunk-DLLIRB63.js.map
@@ -1,4 +1,5 @@
1
1
  // src/protocol.ts
2
+ var FABRIC_BASH_MIDDLEWARE = Symbol.for("pi-fabric:bash-middleware:v1");
2
3
  var FABRIC_PROVIDER_REGISTER_EVENT = "pi-fabric:provider:register:v1";
3
4
  var FABRIC_PROVIDER_DISCOVER_EVENT = "pi-fabric:provider:discover:v1";
4
5
  var FABRIC_COMPONENT_REGISTER_EVENT = "pi-fabric:component:register:v1";
@@ -43,6 +44,7 @@ var readFabricToolResultProxyDetailsV1 = (value) => {
43
44
  };
44
45
 
45
46
  export {
47
+ FABRIC_BASH_MIDDLEWARE,
46
48
  FABRIC_PROVIDER_REGISTER_EVENT,
47
49
  FABRIC_PROVIDER_DISCOVER_EVENT,
48
50
  FABRIC_COMPONENT_REGISTER_EVENT,
@@ -57,4 +59,4 @@ export {
57
59
  FABRIC_TOOL_RESULT_PROXY_KIND,
58
60
  readFabricToolResultProxyDetailsV1
59
61
  };
60
- //# sourceMappingURL=chunk-FAMBJFFF.js.map
62
+ //# sourceMappingURL=chunk-IJL3QK3J.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/protocol.ts"],
4
+ "sourcesContent": ["import type { BashOperations, BashToolOptions, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\n\n/** Host-local opt-in on a bash ToolDefinition; never serialized into guest schemas. */\nexport const FABRIC_BASH_MIDDLEWARE = Symbol.for(\"pi-fabric:bash-middleware:v1\");\n\n/** The extension keeps standalone execution; Fabric owns local process/job lifetime. */\nexport interface FabricBashMiddlewareV1 {\n version: 1;\n options?: Omit<BashToolOptions, \"operations\">;\n /** Filter output before Fabric retains it in previews, results, or background logs. */\n wrapOperations: (operations: BashOperations) => BashOperations;\n}\n\nexport const FABRIC_PROVIDER_REGISTER_EVENT = \"pi-fabric:provider:register:v1\";\nexport const FABRIC_PROVIDER_DISCOVER_EVENT = \"pi-fabric:provider:discover:v1\";\nexport const FABRIC_COMPONENT_REGISTER_EVENT = \"pi-fabric:component:register:v1\";\nexport const FABRIC_COMPONENT_DISCOVER_EVENT = \"pi-fabric:component:discover:v1\";\nexport const FABRIC_PREWALK_REQUEST_EVENT = \"pi-fabric:prewalk:request:v1\";\n\nexport type FabricPrewalkRequestResultV1 =\n | { ok: true }\n | { ok: false; error: string };\n\n/** Host-local request used by queue extensions that need an acknowledged prewalk arm. */\nexport interface FabricPrewalkRequestV1 {\n version: 1;\n context: ExtensionContext;\n claim: () => boolean;\n respond: (result: FabricPrewalkRequestResultV1) => void;\n}\n\nexport const readFabricPrewalkRequestV1 = (\n value: unknown,\n): FabricPrewalkRequestV1 | undefined => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.version !== 1 ||\n typeof record.context !== \"object\" ||\n record.context === null ||\n typeof record.claim !== \"function\" ||\n typeof record.respond !== \"function\"\n ) {\n return undefined;\n }\n return value as FabricPrewalkRequestV1;\n};\n\nexport const FABRIC_PEER_CARDS_EVENT = \"pi-fabric:peers:cards:v1\";\nexport const FABRIC_PEER_AWAIT_SETTLE_EVENT = \"pi-fabric:peer:await-settle:v1\";\n\n/** One root peer session on the project mesh, for pickers and status lines. */\nexport interface FabricPeerCardV1 {\n id: string;\n /** Linear-style project label (e.g. \"PQS-2\") minted by the owning host. */\n label: string;\n status: \"idle\" | \"running\";\n model?: string;\n cwd?: string;\n startedAt: number;\n updatedAt: number;\n pendingMessages: boolean;\n}\n\nexport type FabricPeerCardsResultV1 =\n | { ok: true; cards: FabricPeerCardV1[] }\n | { ok: false; error: string };\n\n/** Host-local request used by queue extensions to enumerate live peer sessions. */\nexport interface FabricPeerCardsRequestV1 {\n version: 1;\n context: ExtensionContext;\n claim: () => boolean;\n respond: (result: FabricPeerCardsResultV1) => void;\n}\n\nexport const readFabricPeerCardsRequestV1 = (\n value: unknown,\n): FabricPeerCardsRequestV1 | undefined => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.version !== 1 ||\n typeof record.context !== \"object\" ||\n record.context === null ||\n typeof record.claim !== \"function\" ||\n typeof record.respond !== \"function\"\n ) {\n return undefined;\n }\n return value as FabricPeerCardsRequestV1;\n};\n\nexport interface FabricPeerSettleProgressV1 {\n waiting: Array<{ label: string; status: \"idle\" | \"running\" }>;\n}\n\nexport type FabricPeerAwaitSettleResultV1 =\n | { ok: true }\n | { ok: false; error: string };\n\n/**\n * Host-local request used by queue extensions to hold dispatch until peer\n * sessions settle. A peer counts as settled once it has been quiet\n * (status-wide) for settledForMs; peers that vanish from the mesh count as\n * settled since they can no longer conflict. Selector matches a peer label\n * (case-insensitive) or exact participant id; omitted waits on all peers.\n */\nexport interface FabricPeerAwaitSettleRequestV1 {\n version: 1;\n context: ExtensionContext;\n selector?: string;\n settledForMs?: number;\n signal?: AbortSignal;\n update?: (progress: FabricPeerSettleProgressV1) => void;\n claim: () => boolean;\n respond: (result: FabricPeerAwaitSettleResultV1) => void;\n}\n\nexport const readFabricPeerAwaitSettleRequestV1 = (\n value: unknown,\n): FabricPeerAwaitSettleRequestV1 | undefined => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n const signal = record.signal as AbortSignal | undefined;\n if (\n record.version !== 1 ||\n typeof record.context !== \"object\" ||\n record.context === null ||\n typeof record.claim !== \"function\" ||\n typeof record.respond !== \"function\" ||\n (record.selector !== undefined && typeof record.selector !== \"string\") ||\n (record.settledForMs !== undefined && typeof record.settledForMs !== \"number\") ||\n (signal !== undefined && typeof signal.aborted !== \"boolean\") ||\n (record.update !== undefined && typeof record.update !== \"function\")\n ) {\n return undefined;\n }\n return value as FabricPeerAwaitSettleRequestV1;\n};\n\n/** Identifies host-side tool lifecycle events replayed for a nested Fabric call. */\nexport const FABRIC_NESTED_TOOL_CALL_ID_PREFIX = \"fabric_\";\n\n/** Discriminant for the transient details envelope on a proxied provider result. */\nexport const FABRIC_TOOL_RESULT_PROXY_KIND = \"pi-fabric.tool-result-proxy.v1\";\n\n/**\n * Host-only middleware details for non-Pi Fabric providers. `result` is the\n * exact value before maxNestedResultChars is enforced and is not persisted as\n * a separate Pi tool-result message.\n */\nexport interface FabricToolResultProxyDetailsV1 {\n kind: typeof FABRIC_TOOL_RESULT_PROXY_KIND;\n ref: string;\n result: unknown;\n}\n\nexport const readFabricToolResultProxyDetailsV1 = (\n value: unknown,\n): FabricToolResultProxyDetailsV1 | undefined => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== FABRIC_TOOL_RESULT_PROXY_KIND ||\n typeof record.ref !== \"string\" ||\n !Object.prototype.hasOwnProperty.call(record, \"result\")\n ) {\n return undefined;\n }\n return record as unknown as FabricToolResultProxyDetailsV1;\n};\n\nexport type FabricRisk = \"read\" | \"write\" | \"execute\" | \"network\" | \"agent\";\nexport type FabricEffectKind = \"none\" | \"scoped\" | \"transactional\" | \"emission\";\nexport type FabricEffectOrdering = \"commutative\" | \"ordered\" | \"unknown\";\n\nexport interface FabricActionEffect {\n kind: FabricEffectKind;\n resources?: string[];\n ordering?: FabricEffectOrdering;\n}\n\n/** MCP tool annotations (Model Context Protocol ToolAnnotations), cached when a runtime surfaces them. */\nexport interface FabricToolAnnotations {\n readOnlyHint?: boolean;\n idempotentHint?: boolean;\n destructiveHint?: boolean;\n openWorldHint?: boolean;\n}\nexport type FabricActivityEntityKind =\n | \"agent\"\n | \"actor\"\n | \"tool\"\n | \"extension\"\n | \"mcp\"\n | \"mesh\"\n | \"task\"\n | \"custom\";\n\nexport type FabricInvocationActivityUpdate =\n | { type: \"progress\"; message: string }\n | { type: \"entity\"; id: string; kind: FabricActivityEntityKind; name?: string }\n | { type: \"metrics\"; tokens?: number; toolCalls?: number; cost?: number };\n\nexport interface FabricMediaBlock {\n type: \"image\";\n data: string;\n mimeType: string;\n}\n\nexport interface FabricActionDescriptor {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n risk: FabricRisk;\n namespace?: string;\n effect?: FabricActionEffect;\n annotations?: FabricToolAnnotations;\n}\n\nexport interface FabricCapabilityActionHead {\n key: string;\n parentKey: string;\n ref: string;\n name: string;\n description: string;\n descriptorHash: string;\n risk: FabricRisk;\n namespace?: string;\n effect?: FabricActionEffect;\n}\n\nexport interface FabricCapabilityProviderHead {\n key: string;\n parentKey: string;\n name: string;\n description: string;\n descriptorHash: string;\n actions: FabricCapabilityActionHead[];\n}\n\nexport interface FabricCapabilityBindingView {\n ref: string;\n provider: string;\n providerBindingId: string;\n generation: number;\n descriptorHash: string;\n}\n\nexport interface FabricCommittedCapabilityView {\n id: string;\n /** Runtime-local digest including provider binding generations. */\n digest: string;\n /** Portable digest of exact refs and descriptor semantics across runtimes. */\n semanticDigest: string;\n bindings: Record<string, FabricCapabilityBindingView>;\n}\n\nexport interface FabricCapabilityResolution {\n satisfied: boolean;\n missing: string[];\n optionalMissing: string[];\n view?: FabricCommittedCapabilityView;\n}\n\nexport interface FabricCapabilityCatalog {\n kind: \"pi-fabric.capability-catalog\";\n version: 1;\n root: {\n key: \"capability:fabric\";\n name: \"Fabric capabilities\";\n description: string;\n descriptorHash: string;\n };\n providers: FabricCapabilityProviderHead[];\n totalActions: number;\n indexedActions: number;\n complete: boolean;\n reasons: string[];\n}\n\nexport interface FabricProviderListRequest {\n namespace?: string;\n query?: string;\n limit?: number;\n}\n\n/** One named action whose arguments should be typed in guest declarations. */\nexport interface FabricNamedActionTypeSource {\n name: string;\n inputSchema: Record<string, unknown>;\n}\n\n/** One MCP server plus the tools to type for `mcp.<server>.*` guest calls. */\nexport interface FabricMcpServerTypeSource {\n server: string;\n tools: FabricNamedActionTypeSource[];\n}\n\nexport type {\n FabricKernel,\n FabricKernelRuntime,\n FabricHostCall,\n FabricSandboxOptions,\n FabricSandboxResult,\n FabricSandboxTerminationReason,\n} from \"./runtime/kernel.js\";\nexport type { FabricPythonRuntime } from \"./config.js\";\n\n/**\n * Live descriptor snapshot the registry hands to the guest declaration\n * builder so dynamic surfaces (mcp, extensions) get argument checking before\n * the sandbox runs. Empty/absent sections keep the loose static declarations.\n */\nexport interface FabricGuestTypeSources {\n mcpServers?: FabricMcpServerTypeSource[];\n extensionTools?: FabricNamedActionTypeSource[];\n}\n\n/**\n * Pre-rendered `declare const` blocks replacing the loose mcp/extensions\n * declaration lines. Values are full replacement text (helpers + declare).\n */\nexport interface FabricDynamicGuestDeclarations {\n mcp?: string;\n extensions?: string;\n}\n\nexport interface FabricInvocationContext {\n cwd: string;\n signal: AbortSignal | undefined;\n parentToolCallId: string;\n nestedToolCallId: string;\n extensionContext: ExtensionContext;\n update(message: string): void;\n activity?(update: FabricInvocationActivityUpdate): void;\n /** Host-supplied inside fabric_exec so agents.handoff schedules the outer-call boundary. */\n deferHandoff?(args: Record<string, unknown>): Record<string, unknown>;\n // Out-of-band image content blocks a provider (currently only pi.read of an\n // image file) wants attached to the call audit, so the single-call render can\n // re-attach them to the fabric_exec result content for pi core's kitty image\n // preview. Bypasses the result char bound that would truncate the base64.\n // `note` is the read tool's own text output (e.g. \"Read image file [image/png]\"),\n // captured after any tool_result patch so a handoff that strips pi's\n // non-vision note has run; used as the single-call body + content text so the\n // preview shows the clean note instead of the swapped description.\n attachMedia?(blocks: FabricMediaBlock[], note?: string): void;\n // Providers call this after mutable tool_call middleware has run so live and\n // durable audit surfaces reflect the arguments actually passed to the tool.\n updateArguments?(args: Record<string, unknown>): void;\n // Ephemeral renderer-only metadata. It is exposed to live Fabric previews but\n // never projected into the durable execution trace.\n attachPreview?(preview: unknown): void;\n capabilityView?: FabricCommittedCapabilityView;\n /** Advisory for ordinary calls; strict components reject concurrent conflicting effects. */\n effectPolicy?: \"advisory\" | \"strict\";\n}\n\nexport interface FabricScopedProviderResult {\n value: unknown;\n dispose(): void | Promise<void>;\n}\n\nexport interface FabricProvider {\n name: string;\n description: string;\n list(\n request: FabricProviderListRequest,\n context: FabricInvocationContext,\n ): Promise<FabricActionDescriptor[]>;\n describe(\n actionName: string,\n context: FabricInvocationContext,\n ): Promise<FabricActionDescriptor | undefined>;\n prepareArguments?(\n actionName: string,\n args: Record<string, unknown>,\n context: FabricInvocationContext,\n ): Record<string, unknown> | Promise<Record<string, unknown>>;\n invoke(\n actionName: string,\n args: Record<string, unknown>,\n context: FabricInvocationContext,\n ): Promise<unknown>;\n acquire?(\n actionName: string,\n args: Record<string, unknown>,\n context: FabricInvocationContext,\n ): Promise<FabricScopedProviderResult>;\n invocationEnded?(parentToolCallId: string): Promise<void>;\n subscribeCatalog?(listener: () => void): () => void;\n close?(): Promise<void>;\n}\n\nexport interface FabricProviderRegistration {\n version: 1;\n provider: FabricProvider;\n overwrite?: boolean;\n}\n\nexport interface FabricProviderDiscovery {\n version: 1;\n register(provider: FabricProvider, options?: { overwrite?: boolean }): void;\n}\n\nexport type {\n ComponentChangeRequest as FabricComponentChangeRequest,\n ComponentChangePlan as FabricComponentChangePlan,\n} from \"./components/control.js\";\n\nexport type {\n FabricCapabilityRequirement,\n FabricComponentChildOptions,\n FabricComponentContext,\n FabricComponentDefinition,\n FabricComponentDiscovery,\n FabricComponentDisposer,\n FabricComponentEffect,\n FabricComponentEffectConflict,\n FabricComponentEffectInfo,\n FabricComponentEffectOptions,\n FabricComponentEffectRegistration,\n FabricComponentEntry,\n FabricComponentGraph,\n FabricComponentGuarantee,\n FabricComponentHandle,\n FabricComponentInfo,\n FabricComponentProviderLease,\n FabricModelGuidance,\n FabricModelGuidanceInfo,\n FabricModelGuidancePlacement,\n FabricModelGuidanceTarget,\n FabricComponentProvision,\n FabricComponentRegistration,\n FabricComponentState,\n FabricComponentStopOptions,\n} from \"./components/types.js\";\n\nexport {\n FABRIC_EXECUTION_GUIDANCE_SLOT,\n MAX_FABRIC_MODEL_GUIDANCE_CONTENT_CHARS,\n MAX_FABRIC_MODEL_GUIDANCE_PER_COMPONENT,\n MAX_FABRIC_MODEL_GUIDANCE_REGISTRATIONS,\n MAX_FABRIC_MODEL_GUIDANCE_SNAPSHOT_CHARS,\n MAX_FABRIC_MODEL_GUIDANCE_TOTAL_CHARS,\n} from \"./components/model-guidance.js\";\n"],
5
+ "mappings": ";AAGO,IAAM,yBAAyB,OAAO,IAAI,8BAA8B;AAUxE,IAAM,iCAAiC;AACvC,IAAM,iCAAiC;AACvC,IAAM,kCAAkC;AACxC,IAAM,kCAAkC;AACxC,IAAM,+BAA+B;AAcrC,IAAM,6BAA6B,CACxC,UACuC;AACvC,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,EAAG,QAAO;AAChF,QAAM,SAAS;AACf,MACE,OAAO,YAAY,KACnB,OAAO,OAAO,YAAY,YAC1B,OAAO,YAAY,QACnB,OAAO,OAAO,UAAU,cACxB,OAAO,OAAO,YAAY,YAC1B;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,IAAM,0BAA0B;AAChC,IAAM,iCAAiC;AA2BvC,IAAM,+BAA+B,CAC1C,UACyC;AACzC,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,EAAG,QAAO;AAChF,QAAM,SAAS;AACf,MACE,OAAO,YAAY,KACnB,OAAO,OAAO,YAAY,YAC1B,OAAO,YAAY,QACnB,OAAO,OAAO,UAAU,cACxB,OAAO,OAAO,YAAY,YAC1B;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AA4BO,IAAM,qCAAqC,CAChD,UAC+C;AAC/C,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,EAAG,QAAO;AAChF,QAAM,SAAS;AACf,QAAM,SAAS,OAAO;AACtB,MACE,OAAO,YAAY,KACnB,OAAO,OAAO,YAAY,YAC1B,OAAO,YAAY,QACnB,OAAO,OAAO,UAAU,cACxB,OAAO,OAAO,YAAY,cACzB,OAAO,aAAa,UAAa,OAAO,OAAO,aAAa,YAC5D,OAAO,iBAAiB,UAAa,OAAO,OAAO,iBAAiB,YACpE,WAAW,UAAa,OAAO,OAAO,YAAY,aAClD,OAAO,WAAW,UAAa,OAAO,OAAO,WAAW,YACzD;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGO,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAatC,IAAM,qCAAqC,CAChD,UAC+C;AAC/C,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,EAAG,QAAO;AAChF,QAAM,SAAS;AACf,MACE,OAAO,SAAS,iCAChB,OAAO,OAAO,QAAQ,YACtB,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,QAAQ,GACtD;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -2,11 +2,11 @@ import {
2
2
  ActionRegistry,
3
3
  NESTED_TOOL_CALL_ID_PREFIX,
4
4
  fabricActionListLimit
5
- } from "../chunks/chunk-JOEFBOFN.js";
5
+ } from "../chunks/chunk-DLLIRB63.js";
6
6
  import "../chunks/chunk-LMAVZN7J.js";
7
7
  import "../chunks/chunk-7TB7NXSL.js";
8
8
  import "../chunks/chunk-PSNXUZMY.js";
9
- import "../chunks/chunk-FAMBJFFF.js";
9
+ import "../chunks/chunk-IJL3QK3J.js";
10
10
  import "../chunks/chunk-D5WKZONW.js";
11
11
  import "../chunks/chunk-G4LADAYY.js";
12
12
  import "../chunks/chunk-YU62DDKK.js";
@@ -0,0 +1,4 @@
1
+ import { type FabricBashMiddlewareV1 } from "../protocol.js";
2
+ /** Invalid opt-ins fail closed: never silently run without an extension's filters. */
3
+ export declare const readFabricBashMiddleware: (definition: object | undefined) => FabricBashMiddlewareV1 | undefined;
4
+ //# sourceMappingURL=shell-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-middleware.d.ts","sourceRoot":"","sources":["../../src/core/shell-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAErF,sFAAsF;AACtF,eAAO,MAAM,wBAAwB,GAAI,YAAY,MAAM,GAAG,SAAS,KAAG,sBAAsB,GAAG,SAkBlG,CAAC"}
@@ -33,7 +33,7 @@ import {
33
33
  expandSkillDirMarkersForRead,
34
34
  resolveFabricIdentity,
35
35
  runFabricHandoffAtBoundary
36
- } from "./chunks/chunk-DUMIK5RZ.js";
36
+ } from "./chunks/chunk-CEXO6ELQ.js";
37
37
  import {
38
38
  AgentTranscriptReader,
39
39
  MAX_WRITE_DIFF_BYTES,
@@ -46,7 +46,7 @@ import {
46
46
  ActionRegistry,
47
47
  fabricActionListLimit,
48
48
  repairActionName
49
- } from "./chunks/chunk-JOEFBOFN.js";
49
+ } from "./chunks/chunk-DLLIRB63.js";
50
50
  import {
51
51
  formatFabricEffectConflict
52
52
  } from "./chunks/chunk-LMAVZN7J.js";
@@ -93,11 +93,12 @@ import {
93
93
  validationMessage
94
94
  } from "./chunks/chunk-PSNXUZMY.js";
95
95
  import {
96
+ FABRIC_BASH_MIDDLEWARE,
96
97
  FABRIC_COMPONENT_DISCOVER_EVENT,
97
98
  FABRIC_PROVIDER_DISCOVER_EVENT,
98
99
  FABRIC_TOOL_RESULT_PROXY_KIND,
99
100
  readFabricToolResultProxyDetailsV1
100
- } from "./chunks/chunk-FAMBJFFF.js";
101
+ } from "./chunks/chunk-IJL3QK3J.js";
101
102
  import {
102
103
  sanitizeFabricMediaText,
103
104
  sanitizeFabricMediaValue
@@ -7240,6 +7241,32 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
7240
7241
  };
7241
7242
  };
7242
7243
 
7244
+ // src/core/shell-middleware.ts
7245
+ var readFabricBashMiddleware = (definition) => {
7246
+ if (!definition || !(FABRIC_BASH_MIDDLEWARE in definition)) return void 0;
7247
+ const value = Reflect.get(definition, FABRIC_BASH_MIDDLEWARE);
7248
+ const invalid = () => {
7249
+ throw new Error("Invalid Fabric bash middleware v1; refusing to bypass shell protection");
7250
+ };
7251
+ if (!value || typeof value !== "object" || Array.isArray(value)) return invalid();
7252
+ const record = value;
7253
+ if (record.version !== 1 || typeof record.wrapOperations !== "function") return invalid();
7254
+ if (record.options !== void 0) {
7255
+ if (!record.options || typeof record.options !== "object" || Array.isArray(record.options)) return invalid();
7256
+ const options = record.options;
7257
+ const fields = {
7258
+ commandPrefix: "string",
7259
+ shellPath: "string",
7260
+ exposeSessionEnvironment: "boolean",
7261
+ spawnHook: "function"
7262
+ };
7263
+ for (const [key, option] of Object.entries(options)) {
7264
+ if (!Object.hasOwn(fields, key) || option !== void 0 && typeof option !== fields[key]) return invalid();
7265
+ }
7266
+ }
7267
+ return value;
7268
+ };
7269
+
7243
7270
  // src/providers/pi-bash-cwd.ts
7244
7271
  import { accessSync, constants, statSync } from "node:fs";
7245
7272
  import path7 from "node:path";
@@ -7588,17 +7615,28 @@ var PiToolsProvider = class {
7588
7615
  const name = actionName;
7589
7616
  if (this.#allowedTools && !this.#allowedTools.has(name)) return void 0;
7590
7617
  const override = await this.#capturedTools?.describe(name, _context);
7591
- if (override) return { ...override, namespace: "extension-override" };
7618
+ if (override) {
7619
+ if (this.#bashMiddleware(name)) {
7620
+ return {
7621
+ ...override,
7622
+ description: `${override.description} Shell execution and background jobs are managed by Fabric with the extension's middleware.`,
7623
+ inputSchema: this.#descriptor(name, this.#tools[name]).inputSchema,
7624
+ namespace: "extension-middleware"
7625
+ };
7626
+ }
7627
+ return { ...override, namespace: "extension-override" };
7628
+ }
7592
7629
  const tool = this.#tools[name];
7593
7630
  if (!tool || this.#requireCapturedOverrides) return void 0;
7594
7631
  return this.#descriptor(name, tool);
7595
7632
  }
7596
7633
  prepareArguments(actionName, args) {
7597
7634
  this.#assertAllowed(actionName);
7598
- const definition = this.#catalog?.get(actionName)?.definition ?? this.#tools[actionName];
7635
+ const middleware = this.#bashMiddleware(actionName);
7636
+ const definition = (middleware ? void 0 : this.#catalog?.get(actionName)?.definition) ?? this.#tools[actionName];
7599
7637
  const properties = definition?.parameters?.properties ?? {};
7600
7638
  args = normalizePiArguments(actionName, args, Object.keys(properties));
7601
- if (this.#catalog?.get(actionName)) {
7639
+ if (this.#catalog?.get(actionName) && !middleware) {
7602
7640
  return this.#capturedTools.prepareArguments(actionName, args);
7603
7641
  }
7604
7642
  const tool = this.#tools[actionName];
@@ -7669,15 +7707,26 @@ var PiToolsProvider = class {
7669
7707
  const cwd = args[PI_BASH_CWD_KEY];
7670
7708
  return isPiShellToolName(name) && typeof cwd === "string" ? { ...context, cwd } : context;
7671
7709
  }
7710
+ #bashMiddleware(name) {
7711
+ if (name !== "bash" || this.#requireCapturedOverrides) return void 0;
7712
+ return readFabricBashMiddleware(this.#catalog?.get(name)?.definition);
7713
+ }
7672
7714
  #shellHangMs() {
7673
7715
  const value = this.#getShellHangMs?.() ?? DEFAULT_SHELL_HANG_MS;
7674
7716
  return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : DEFAULT_SHELL_HANG_MS;
7675
7717
  }
7676
- #trackedShellDefinition(name, args, job) {
7718
+ #trackedShellDefinition(name, args, job, middleware) {
7677
7719
  const cwd = typeof args[PI_BASH_CWD_KEY] === "string" ? args[PI_BASH_CWD_KEY] : this.#cwd;
7678
7720
  if (name === "bash") {
7721
+ const options = middleware?.options;
7722
+ const local = createLocalBashOperations(options?.shellPath !== void 0 ? { shellPath: options.shellPath } : void 0);
7723
+ const operations = middleware ? middleware.wrapOperations(local) : local;
7724
+ if (!operations || typeof operations.exec !== "function") {
7725
+ throw new Error("Invalid Fabric bash middleware operations; refusing to bypass shell protection");
7726
+ }
7679
7727
  return createBashToolDefinition2(cwd, {
7680
- operations: trackShellOperations(createLocalBashOperations(), job, "bash")
7728
+ ...options,
7729
+ operations: trackShellOperations(operations, job, "bash")
7681
7730
  });
7682
7731
  }
7683
7732
  const create = Reflect.get(PiCodingAgent2, "createPowerShellToolDefinition");
@@ -7689,7 +7738,7 @@ var PiToolsProvider = class {
7689
7738
  operations: trackShellOperations(operationsFactory(), job, "powershell")
7690
7739
  });
7691
7740
  }
7692
- async #runExecute(name, tool, args, context, onUpdate) {
7741
+ async #runExecute(name, tool, args, context, onUpdate, middleware) {
7693
7742
  if (!isPiShellToolName(name) || this.#requireCapturedOverrides) {
7694
7743
  return await runAbortable(
7695
7744
  context.signal,
@@ -7702,15 +7751,21 @@ var PiToolsProvider = class {
7702
7751
  )
7703
7752
  );
7704
7753
  }
7705
- return this.#executeHungShell(name, args, context, onUpdate);
7754
+ return this.#executeHungShell(name, args, context, onUpdate, middleware);
7706
7755
  }
7707
- async #executeHungShell(name, args, context, onUpdate) {
7756
+ async #executeHungShell(name, args, context, onUpdate, middleware) {
7708
7757
  const command = typeof args.command === "string" ? args.command : "";
7709
7758
  const background = args.background === true;
7710
7759
  const executeArgs = background || "background" in args ? (({ background: _ignored, ...rest }) => rest)(args) : args;
7711
7760
  const job = this.#shellJobs.begin(name, command);
7761
+ let tool;
7762
+ try {
7763
+ tool = this.#trackedShellDefinition(name, executeArgs, job, middleware);
7764
+ } catch (error) {
7765
+ await job.finish(null);
7766
+ throw error;
7767
+ }
7712
7768
  void job.readPid();
7713
- const tool = this.#trackedShellDefinition(name, executeArgs, job);
7714
7769
  let spilled = false;
7715
7770
  const outcome = await raceShellHang({
7716
7771
  hangMs: background ? 0 : this.#shellHangMs(),
@@ -7762,14 +7817,15 @@ var PiToolsProvider = class {
7762
7817
  const name = actionName;
7763
7818
  this.#assertAllowed(name);
7764
7819
  if (!this.#requireCapturedOverrides && !this.#tools[name]) throw new Error(`Unknown Pi tool: ${actionName}`);
7765
- if (name === "bash" && !this.#requireCapturedOverrides) {
7820
+ if (name === "bash" && !this.#requireCapturedOverrides && !this.#catalog?.get(name)) {
7766
7821
  const intercepted = await tryExecuteGitWorktreeAdd(args, this.#cwd);
7767
7822
  if (intercepted) {
7768
7823
  this.#attachPreview(name, intercepted, args, context);
7769
7824
  return this.#normalizeResult(name, intercepted, args);
7770
7825
  }
7771
7826
  }
7772
- if (this.#catalog?.get(name)) {
7827
+ const middleware = this.#bashMiddleware(name);
7828
+ if (this.#catalog?.get(name) && !middleware) {
7773
7829
  const result = await this.#capturedTools.invoke(name, args, context);
7774
7830
  this.#attachReadMedia(name, result, context);
7775
7831
  this.#attachReadNote(name, result, context);
@@ -7784,7 +7840,8 @@ var PiToolsProvider = class {
7784
7840
  tool,
7785
7841
  args,
7786
7842
  context,
7787
- (partialResult) => this.#attachPartialPreview(name, partialResult, args, context)
7843
+ (partialResult) => this.#attachPartialPreview(name, partialResult, args, context),
7844
+ middleware
7788
7845
  ).catch((error) => {
7789
7846
  throwIfAborted(context.signal);
7790
7847
  throw isPiShellToolName(name) ? classifyPiBashError(error) : error;
@@ -7794,7 +7851,7 @@ var PiToolsProvider = class {
7794
7851
  this.#attachPreview(name, result, args, context);
7795
7852
  return this.#normalizeResult(name, result, args);
7796
7853
  }
7797
- return this.#invokeWithEvents(name, tool, args, context, runner);
7854
+ return this.#invokeWithEvents(name, tool, args, context, runner, middleware);
7798
7855
  }
7799
7856
  // Replay the agent-core tool-execution lifecycle for a nested pi.* call, so
7800
7857
  // extensions that hook tool_call / tool_result / tool_execution_* see pi
@@ -7804,7 +7861,7 @@ var PiToolsProvider = class {
7804
7861
  // tools. tool_result patches (content/details/isError) are applied, so
7805
7862
  // extensions like pi-vision-handoff can replace image blocks with text
7806
7863
  // descriptions before the result returns to the sandbox.
7807
- async #invokeWithEvents(name, tool, args, context, runner) {
7864
+ async #invokeWithEvents(name, tool, args, context, runner, middleware) {
7808
7865
  const toolCallId = context.nestedToolCallId;
7809
7866
  await runAbortable(context.signal, () => runner.emit({
7810
7867
  type: "tool_execution_start",
@@ -7845,7 +7902,8 @@ var PiToolsProvider = class {
7845
7902
  partialResult
7846
7903
  }))
7847
7904
  ).catch(() => void 0);
7848
- }
7905
+ },
7906
+ middleware
7849
7907
  );
7850
7908
  } catch (error) {
7851
7909
  thrown = isPiShellToolName(name) && executionStarted ? classifyPiBashError(error) : error;