pi-fabric 0.94.0 → 0.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +1 -0
  2. package/dist/chunks/{chunk-PVSADHF4.js → chunk-4BW7JF2W.js} +1 -1
  3. package/dist/chunks/{chunk-AAHHYDTO.js → chunk-4NF3S3NM.js} +1 -1
  4. package/dist/chunks/{chunk-AAHHYDTO.js.map → chunk-4NF3S3NM.js.map} +2 -2
  5. package/dist/chunks/{chunk-7VCPGNDY.js → chunk-B2IVWASP.js} +2 -2
  6. package/dist/chunks/{chunk-H6RSDEWW.js → chunk-F72MAIQY.js} +31 -367
  7. package/dist/chunks/chunk-F72MAIQY.js.map +7 -0
  8. package/dist/chunks/{chunk-W3FRE4VI.js → chunk-FDIEQEXS.js} +2 -2
  9. package/dist/chunks/{chunk-RTXZHKMS.js → chunk-QNMQJNXE.js} +7 -3
  10. package/dist/chunks/chunk-QNMQJNXE.js.map +7 -0
  11. package/dist/chunks/{chunk-FLKGJPCN.js → chunk-ULQFHZJE.js} +4 -1
  12. package/dist/chunks/chunk-ULQFHZJE.js.map +7 -0
  13. package/dist/chunks/{chunk-7VAZ4N43.js → chunk-V3PIO7QL.js} +2 -2
  14. package/dist/chunks/{chunk-HCBV735P.js → chunk-YC3QQDZ3.js} +2 -2
  15. package/dist/chunks/{chunk-DBLT6J5B.js → chunk-ZRI433JP.js} +6 -6
  16. package/dist/chunks/{chunk-36B4AIRT.js → chunk-ZTXZ6JDO.js} +2 -2
  17. package/dist/commands/fabric.d.ts.map +1 -1
  18. package/dist/components/configuration.js +2 -3
  19. package/dist/core/shell-inbox.d.ts +13 -0
  20. package/dist/core/shell-inbox.d.ts.map +1 -0
  21. package/dist/core/shell-jobs.d.ts +42 -7
  22. package/dist/core/shell-jobs.d.ts.map +1 -1
  23. package/dist/core/shell-limits.d.ts +3 -0
  24. package/dist/core/shell-limits.d.ts.map +1 -0
  25. package/dist/core/shell-monitor.d.ts +51 -0
  26. package/dist/core/shell-monitor.d.ts.map +1 -0
  27. package/dist/fabric-runtime-state.d.ts +1 -1
  28. package/dist/fabric-runtime-state.d.ts.map +1 -1
  29. package/dist/fabric-runtime-state.js +940 -179
  30. package/dist/fabric-runtime-state.js.map +4 -4
  31. package/dist/index.js +131 -20
  32. package/dist/index.js.map +3 -3
  33. package/dist/jev.js +1 -1
  34. package/dist/providers/jev-provider.js +1 -1
  35. package/dist/providers/pi-tools-provider.d.ts.map +1 -1
  36. package/dist/providers/tasks-provider.d.ts +12 -0
  37. package/dist/providers/tasks-provider.d.ts.map +1 -0
  38. package/dist/residency/host.js +4 -4
  39. package/dist/residency/pi-entry.js +4 -4
  40. package/dist/runtime/core-override-guest-types.js +2 -2
  41. package/dist/runtime/guest-types.d.ts +1 -1
  42. package/dist/runtime/guest-types.d.ts.map +1 -1
  43. package/dist/runtime/guest-types.js +1 -1
  44. package/dist/runtime/monty-runtime.js +1 -2
  45. package/dist/runtime/monty-runtime.js.map +1 -1
  46. package/dist/runtime/typescript-kernel.js +2 -2
  47. package/dist/runtime-state-builtins.d.ts +1 -1
  48. package/dist/runtime-state-builtins.d.ts.map +1 -1
  49. package/dist/ui/controller.d.ts +1 -0
  50. package/dist/ui/controller.d.ts.map +1 -1
  51. package/dist/ui/conversation-targets.js +2 -2
  52. package/dist/ui/conversation.js +7 -7
  53. package/dist/ui/dashboard.js +6 -6
  54. package/dist/ui/highlight.d.ts.map +1 -1
  55. package/dist/ui/languages/bend.d.ts +4 -0
  56. package/dist/ui/languages/bend.d.ts.map +1 -0
  57. package/dist/ui/languages/bend.js +73 -0
  58. package/dist/ui/languages/bend.js.map +7 -0
  59. package/dist/ui/settings.js +1 -2
  60. package/dist/ui/settings.js.map +1 -1
  61. package/dist/ui/shell-hang-keys.d.ts.map +1 -1
  62. package/dist/ui/shell-tasks.d.ts +22 -0
  63. package/dist/ui/shell-tasks.d.ts.map +1 -0
  64. package/dist/ui/shell-tasks.js +161 -0
  65. package/dist/ui/shell-tasks.js.map +7 -0
  66. package/dist/ui/shiki-catalog.d.ts +2 -1
  67. package/dist/ui/shiki-catalog.d.ts.map +1 -1
  68. package/dist/ui/snapshot.d.ts.map +1 -1
  69. package/dist/ui/types.d.ts +1 -0
  70. package/dist/ui/types.d.ts.map +1 -1
  71. package/dist/ui/widget.d.ts.map +1 -1
  72. package/docs/background-tasks.md +122 -0
  73. package/docs/interface.md +7 -0
  74. package/docs/shell-middleware.md +2 -2
  75. package/package.json +1 -1
  76. package/skillsets/typescript/fabric-exec/SKILL.md +4 -2
  77. package/dist/chunks/chunk-FLKGJPCN.js.map +0 -7
  78. package/dist/chunks/chunk-H6RSDEWW.js.map +0 -7
  79. package/dist/chunks/chunk-RTXZHKMS.js.map +0 -7
  80. /package/dist/chunks/{chunk-PVSADHF4.js.map → chunk-4BW7JF2W.js.map} +0 -0
  81. /package/dist/chunks/{chunk-7VCPGNDY.js.map → chunk-B2IVWASP.js.map} +0 -0
  82. /package/dist/chunks/{chunk-W3FRE4VI.js.map → chunk-FDIEQEXS.js.map} +0 -0
  83. /package/dist/chunks/{chunk-7VAZ4N43.js.map → chunk-V3PIO7QL.js.map} +0 -0
  84. /package/dist/chunks/{chunk-HCBV735P.js.map → chunk-YC3QQDZ3.js.map} +0 -0
  85. /package/dist/chunks/{chunk-DBLT6J5B.js.map → chunk-ZRI433JP.js.map} +0 -0
  86. /package/dist/chunks/{chunk-36B4AIRT.js.map → chunk-ZTXZ6JDO.js.map} +0 -0
package/README.md CHANGED
@@ -133,6 +133,7 @@ Fabric includes a live activity surface in Pi:
133
133
  - A compact widget above the chat (like `pi-supervisor`) whose header follows the current phase while its rows show active/completed agents, active actors, and their recent nested tool or code-change activity.
134
134
  - `/fabric` (or `/fabric dashboard`): opens the **Activity** and **Topology** views. The user-facing Pi session appears as **Main**. You can queue or steer participants and inspect the project topology.
135
135
  - `/fabric settings`: mirrors Pi's `/settings` and writes changes to `fabric.json`. TUI hosts get the searchable settings component; RPC hosts get the same nested sections, value/input/model pickers, list editors, and project/global save scopes through native dialog primitives.
136
+ - Code previews automatically highlight `.bend` files using Bend 2 syntax, including laws and proof terms. The grammar loads only when needed, with no Bend installation required.
136
137
  - `Tool display` (`compact` by default, or `full`) is configured under `/fabric settings` → **UI**; compact elevates the declared display intent, hides the outer program, and applies to the current transcript immediately. Pi's tool-expand keybinding (`ctrl+o` by default) expands a compact card to the full transcript.
137
138
 
138
139
  See the [interface & commands reference](docs/interface.md) for every view, keybinding, and slash command.
@@ -448,4 +448,4 @@ export {
448
448
  JEV_ACTION_DESCRIPTORS,
449
449
  JevProvider
450
450
  };
451
- //# sourceMappingURL=chunk-PVSADHF4.js.map
451
+ //# sourceMappingURL=chunk-4BW7JF2W.js.map
@@ -21,4 +21,4 @@ export {
21
21
  isActiveStatus,
22
22
  orderAgentsByCreation
23
23
  };
24
- //# sourceMappingURL=chunk-AAHHYDTO.js.map
24
+ //# sourceMappingURL=chunk-4NF3S3NM.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/types.ts"],
4
- "sourcesContent": ["import type { FabricActorInfo, FabricActorMessage, GlobalActorDefinition } from \"../actors/types.js\";\nimport type { FabricActivityRun } from \"../activity/types.js\";\nimport type { FabricComponentGraph } from \"../components/types.js\";\nimport type { MeshEvent } from \"../mesh/store.js\";\nimport type { FabricMainAgentInfo } from \"../main-agent.js\";\nimport type {\n FabricParticipantCapability,\n FabricParticipantInfo,\n FabricParticipantKind,\n FabricParticipantResidency,\n FabricPeerInfo,\n} from \"../topology/types.js\";\nimport type { AgentUsage } from \"../agents/types.js\";\n\nexport type FabricUiMain = FabricMainAgentInfo;\nexport type FabricUiPeer = FabricPeerInfo;\n\nexport interface FabricUiAgent {\n id: string;\n name: string;\n status: string;\n runner?: \"pi\" | \"claude\" | \"veda\";\n residency?: FabricParticipantResidency;\n transport: string;\n cwd: string;\n task?: string;\n model?: string;\n thinking?: string;\n currentTool?: string;\n startedAt?: number;\n updatedAt?: number;\n finishedAt?: number;\n turns?: number;\n toolCalls?: number;\n usage?: AgentUsage;\n text?: string;\n value?: unknown;\n error?: string;\n logFile?: string;\n attachCommand?: string;\n branch?: string;\n worktree?: string;\n actorId?: string;\n actorName?: string;\n runId?: string;\n phaseId?: string;\n parentId?: string;\n nestingDepth?: number;\n rootId?: string;\n ownerHostId?: string;\n local?: boolean;\n stale?: boolean;\n participantKind?: FabricParticipantKind;\n capabilities?: FabricParticipantCapability[];\n}\n\nexport interface FabricUiActor extends FabricActorInfo {\n /** The actor's default instruction (persona text); shown and edited in the dashboard. */\n instructions: string;\n recentMessages: FabricActorMessage[];\n ownerHostId?: string;\n local?: boolean;\n worker?: FabricUiAgent;\n}\n\nexport interface FabricUiStateEntry {\n key: string;\n label: string;\n status: string;\n owner?: string;\n detail?: string;\n value: unknown;\n version: number;\n updatedAt: number;\n}\n\nexport interface FabricDashboardSnapshot {\n now: number;\n widgetDismissedAt?: number;\n runs: FabricActivityRun[];\n main: FabricUiMain;\n peers: FabricUiPeer[];\n participants?: FabricParticipantInfo[];\n agents: FabricUiAgent[];\n actors: FabricUiActor[];\n componentGraph: FabricComponentGraph;\n /** Project-independent actor templates from the global registry. */\n globalActors: GlobalActorDefinition[];\n state: FabricUiStateEntry[];\n events: MeshEvent[];\n}\n\nexport const activeStatuses = new Set([\n \"queued\",\n \"pending\",\n \"ready\",\n \"claimed\",\n \"running\",\n \"in_progress\",\n \"blocked\",\n \"loading\",\n \"active\",\n \"unloading\",\n]);\n\nexport const isActiveStatus = (status: string): boolean => activeStatuses.has(status);\n\nexport const orderAgentsByCreation = (agents: FabricUiAgent[]): FabricUiAgent[] =>\n agents\n .map((agent, index) => ({ agent, index }))\n .sort(\n (left, right) =>\n (left.agent.startedAt ?? Number.MAX_SAFE_INTEGER) -\n (right.agent.startedAt ?? Number.MAX_SAFE_INTEGER) ||\n left.index - right.index,\n )\n .map(({ agent }) => agent);\n"],
5
- "mappings": ";AA4FO,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,iBAAiB,CAAC,WAA4B,eAAe,IAAI,MAAM;AAE7E,IAAM,wBAAwB,CAAC,WACpC,OACG,IAAI,CAAC,OAAO,WAAW,EAAE,OAAO,MAAM,EAAE,EACxC;AAAA,EACC,CAAC,MAAM,WACJ,KAAK,MAAM,aAAa,OAAO,qBAC7B,MAAM,MAAM,aAAa,OAAO,qBACnC,KAAK,QAAQ,MAAM;AACvB,EACC,IAAI,CAAC,EAAE,MAAM,MAAM,KAAK;",
4
+ "sourcesContent": ["import type { FabricActorInfo, FabricActorMessage, GlobalActorDefinition } from \"../actors/types.js\";\nimport type { FabricActivityRun } from \"../activity/types.js\";\nimport type { FabricComponentGraph } from \"../components/types.js\";\nimport type { MeshEvent } from \"../mesh/store.js\";\nimport type { FabricMainAgentInfo } from \"../main-agent.js\";\nimport type {\n FabricParticipantCapability,\n FabricParticipantInfo,\n FabricParticipantKind,\n FabricParticipantResidency,\n FabricPeerInfo,\n} from \"../topology/types.js\";\nimport type { AgentUsage } from \"../agents/types.js\";\n\nexport type FabricUiMain = FabricMainAgentInfo;\nexport type FabricUiPeer = FabricPeerInfo;\n\nexport interface FabricUiAgent {\n id: string;\n name: string;\n status: string;\n runner?: \"pi\" | \"claude\" | \"veda\";\n residency?: FabricParticipantResidency;\n transport: string;\n cwd: string;\n task?: string;\n model?: string;\n thinking?: string;\n currentTool?: string;\n startedAt?: number;\n updatedAt?: number;\n finishedAt?: number;\n turns?: number;\n toolCalls?: number;\n usage?: AgentUsage;\n text?: string;\n value?: unknown;\n error?: string;\n logFile?: string;\n attachCommand?: string;\n branch?: string;\n worktree?: string;\n actorId?: string;\n actorName?: string;\n runId?: string;\n phaseId?: string;\n parentId?: string;\n nestingDepth?: number;\n rootId?: string;\n ownerHostId?: string;\n local?: boolean;\n stale?: boolean;\n participantKind?: FabricParticipantKind;\n capabilities?: FabricParticipantCapability[];\n}\n\nexport interface FabricUiActor extends FabricActorInfo {\n /** The actor's default instruction (persona text); shown and edited in the dashboard. */\n instructions: string;\n recentMessages: FabricActorMessage[];\n ownerHostId?: string;\n local?: boolean;\n worker?: FabricUiAgent;\n}\n\nexport interface FabricUiStateEntry {\n key: string;\n label: string;\n status: string;\n owner?: string;\n detail?: string;\n value: unknown;\n version: number;\n updatedAt: number;\n}\n\nexport interface FabricDashboardSnapshot {\n shells?: import(\"../core/shell-jobs.js\").FabricShellJobInfo[];\n now: number;\n widgetDismissedAt?: number;\n runs: FabricActivityRun[];\n main: FabricUiMain;\n peers: FabricUiPeer[];\n participants?: FabricParticipantInfo[];\n agents: FabricUiAgent[];\n actors: FabricUiActor[];\n componentGraph: FabricComponentGraph;\n /** Project-independent actor templates from the global registry. */\n globalActors: GlobalActorDefinition[];\n state: FabricUiStateEntry[];\n events: MeshEvent[];\n}\n\nexport const activeStatuses = new Set([\n \"queued\",\n \"pending\",\n \"ready\",\n \"claimed\",\n \"running\",\n \"in_progress\",\n \"blocked\",\n \"loading\",\n \"active\",\n \"unloading\",\n]);\n\nexport const isActiveStatus = (status: string): boolean => activeStatuses.has(status);\n\nexport const orderAgentsByCreation = (agents: FabricUiAgent[]): FabricUiAgent[] =>\n agents\n .map((agent, index) => ({ agent, index }))\n .sort(\n (left, right) =>\n (left.agent.startedAt ?? Number.MAX_SAFE_INTEGER) -\n (right.agent.startedAt ?? Number.MAX_SAFE_INTEGER) ||\n left.index - right.index,\n )\n .map(({ agent }) => agent);\n"],
5
+ "mappings": ";AA6FO,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,iBAAiB,CAAC,WAA4B,eAAe,IAAI,MAAM;AAE7E,IAAM,wBAAwB,CAAC,WACpC,OACG,IAAI,CAAC,OAAO,WAAW,EAAE,OAAO,MAAM,EAAE,EACxC;AAAA,EACC,CAAC,MAAM,WACJ,KAAK,MAAM,aAAa,OAAO,qBAC7B,MAAM,MAAM,aAAa,OAAO,qBACnC,KAAK,QAAQ,MAAM;AACvB,EACC,IAAI,CAAC,EAAE,MAAM,MAAM,KAAK;",
6
6
  "names": []
7
7
  }
@@ -9,7 +9,7 @@ import {
9
9
  agentParticipantRecords,
10
10
  loadModelUsage,
11
11
  useBudgetLedger
12
- } from "./chunk-DBLT6J5B.js";
12
+ } from "./chunk-ZRI433JP.js";
13
13
  import {
14
14
  RESIDENT_HOST_FORMAT,
15
15
  residentDeliveryPrefix,
@@ -691,4 +691,4 @@ if (isMain) {
691
691
  export {
692
692
  runResidentHostFromConfigPath
693
693
  };
694
- //# sourceMappingURL=chunk-7VCPGNDY.js.map
694
+ //# sourceMappingURL=chunk-B2IVWASP.js.map
@@ -2,10 +2,6 @@ import {
2
2
  defaultCodePreviewSettings,
3
3
  normalizeCodePreviewSettings
4
4
  } from "./chunk-OKOHRXGF.js";
5
- import {
6
- closeScratch,
7
- createScratch
8
- } from "./chunk-RAO6TQFP.js";
9
5
  import {
10
6
  renameAtomic
11
7
  } from "./chunk-KAD7UWT2.js";
@@ -27,352 +23,25 @@ import {
27
23
  PI_CORE_TOOL_NAME_SET
28
24
  } from "./chunk-YU62DDKK.js";
29
25
 
30
- // src/core/shell-jobs.ts
31
- import fs from "node:fs";
32
- import { readFile, unlink } from "node:fs/promises";
33
- import { tmpdir } from "node:os";
34
- import path from "node:path";
35
- import { randomUUID } from "node:crypto";
26
+ // src/core/shell-limits.ts
36
27
  var DEFAULT_SHELL_HANG_MS = 12e4;
37
28
  var SHELL_HANG_MAX_MS = 6e5;
38
- var SHELL_HANG_SNAPSHOT_BYTES = 8e3;
39
- var SHELL_TAIL_BYTES = 1024 * 1024;
40
- var SHELL_LOG_BYTES = 8 * 1024 * 1024;
41
- var SHELL_COMPLETED_HANDLES = 256;
42
- var SHELL_COMPLETED_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
43
- var LOG_HEADER = "[Bounded shell log: starts with retained pre-spill tail; 8 MiB total cap, then further output is omitted. Not a full-output archive.]\n";
44
- var LOG_TRUNCATED = "\n[Shell log truncated: disk limit reached; subsequent output omitted.]\n";
45
- var posixQuote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
46
- var powershellQuote = (value) => "'" + value.replaceAll("'", "''") + "'";
47
- var wrapShellCommandForPid = (command, pidPath, tool) => tool === "powershell" ? `Set-Content -LiteralPath ${powershellQuote(pidPath)} -Value $PID
48
- ${command}` : `printf '%s\\n' "$(cat /proc/$$/winpid 2>/dev/null || printf '%s' "$$")" > ${posixQuote(pidPath)}
49
- ${command}`;
50
- var formatShellHangNotice = (input) => {
51
- const seconds = Math.max(1, Math.round(input.elapsedMs / 1e3));
52
- const pid = input.pid !== void 0 ? ` (pid ${input.pid})` : "";
53
- return `[Still running after ${seconds}s${pid}. Bounded live output (may be truncated): ${input.logPath}]`;
54
- };
55
- var appendShellHangNotice = (output, notice) => output ? `${output}
56
-
57
- ${notice}` : notice;
58
- var parseShellPid = (text) => {
59
- const pid = Number(text.trim());
60
- return Number.isSafeInteger(pid) && pid > 1 ? pid : void 0;
61
- };
62
- var FabricShellJob = class {
63
- constructor(tool, command, onFinish, tempRoot) {
64
- this.onFinish = onFinish;
65
- this.id = randomUUID();
66
- this.tool = tool;
67
- this.command = command;
68
- this.#directory = createScratch("shell", tempRoot);
69
- this.pidPath = path.join(this.#directory, "child.pid");
70
- }
71
- id;
72
- tool;
73
- command;
74
- abort = new AbortController();
75
- startedAt = Date.now();
76
- pidPath;
77
- pid;
78
- logPath;
79
- spilled = false;
80
- finished = false;
81
- spilledAt;
82
- finishedAt;
83
- exitCode;
84
- status = "running";
85
- #tail = Buffer.alloc(0);
86
- #omitted = false;
87
- #directory;
88
- #descriptor;
89
- #logBytes = 0;
90
- #logTruncated = false;
91
- #spill = new AbortController();
92
- #pidRead;
93
- append(data) {
94
- if (this.finished) return;
95
- const keep = Math.max(0, SHELL_TAIL_BYTES - data.length);
96
- if (this.#tail.length + data.length > SHELL_TAIL_BYTES) this.#omitted = true;
97
- this.#tail = Buffer.concat([
98
- this.#tail.subarray(Math.max(0, this.#tail.length - keep)),
99
- data.subarray(Math.max(0, data.length - SHELL_TAIL_BYTES))
100
- ]);
101
- this.#writeLog(data);
102
- }
103
- #writeLog(data) {
104
- if (this.#descriptor === void 0 || this.#logTruncated) return;
105
- const available = Math.max(0, SHELL_LOG_BYTES - Buffer.byteLength(LOG_TRUNCATED) - this.#logBytes);
106
- try {
107
- const chunk = data.subarray(0, available);
108
- let offset = 0;
109
- while (offset < chunk.length) {
110
- const written = fs.writeSync(this.#descriptor, chunk, offset);
111
- if (written <= 0) throw new Error("Shell log write made no progress");
112
- offset += written;
113
- }
114
- this.#logBytes += chunk.length;
115
- if (chunk.length < data.length) {
116
- fs.writeSync(this.#descriptor, LOG_TRUNCATED);
117
- this.#logTruncated = true;
118
- }
119
- } catch {
120
- try {
121
- fs.closeSync(this.#descriptor);
122
- } catch {
123
- }
124
- this.#descriptor = void 0;
125
- this.#logTruncated = true;
126
- }
127
- }
128
- snapshotText(maxBytes = SHELL_HANG_SNAPSHOT_BYTES) {
129
- const limit = Number.isFinite(maxBytes) ? Math.max(0, Math.floor(maxBytes)) : SHELL_TAIL_BYTES;
130
- const slice = this.#tail.subarray(Math.max(0, this.#tail.length - limit));
131
- const truncated = this.#omitted || slice.length < this.#tail.length;
132
- return `${truncated ? "[Output truncated; retained tail follows]\n" : ""}${slice.toString("utf8")}`;
133
- }
134
- async persistLog() {
135
- if (this.logPath) return this.logPath;
136
- if (this.finished) throw new Error("Shell job finished before a log was requested");
137
- const logPath = path.join(this.#directory, "output.log");
138
- try {
139
- this.#descriptor = fs.openSync(logPath, "wx", 384);
140
- fs.writeSync(this.#descriptor, LOG_HEADER);
141
- this.#logBytes = Buffer.byteLength(LOG_HEADER);
142
- if (this.#omitted) this.#writeLog(Buffer.from("[Pre-spill output truncated: only the last 1 MiB was retained.]\n"));
143
- this.#writeLog(this.#tail);
144
- this.logPath = logPath;
145
- return logPath;
146
- } catch (error) {
147
- if (this.#descriptor !== void 0) {
148
- try {
149
- fs.closeSync(this.#descriptor);
150
- } catch {
151
- }
152
- }
153
- this.#descriptor = void 0;
154
- try {
155
- fs.unlinkSync(logPath);
156
- } catch {
157
- }
158
- throw error;
159
- }
160
- }
161
- async readPid() {
162
- if (this.pid !== void 0) return this.pid;
163
- this.#pidRead ??= (async () => {
164
- for (let attempt = 0; attempt < 25; attempt += 1) {
165
- try {
166
- const pid = parseShellPid(await readFile(this.pidPath, "utf8"));
167
- if (pid !== void 0) {
168
- this.pid = pid;
169
- return pid;
170
- }
171
- } catch {
172
- }
173
- await new Promise((resolve) => setTimeout(resolve, 10));
174
- }
175
- return void 0;
176
- })();
177
- return this.#pidRead;
178
- }
179
- spill() {
180
- if (this.spilled || this.finished) return;
181
- this.spilled = true;
182
- this.spilledAt = Date.now();
183
- this.status = "spilled";
184
- if (!this.#spill.signal.aborted) this.#spill.abort();
185
- }
186
- whenSpill() {
187
- if (this.spilled || this.#spill.signal.aborted) return Promise.resolve();
188
- return new Promise((resolve) => {
189
- this.#spill.signal.addEventListener("abort", () => resolve(), { once: true });
190
- });
191
- }
192
- async finish(exitCode, footer) {
193
- if (this.finished) return;
194
- const persistence = this.spilled && !this.logPath ? this.persistLog() : void 0;
195
- this.finished = true;
196
- await persistence?.catch(() => void 0);
197
- this.finishedAt = Date.now();
198
- if (exitCode !== void 0) this.exitCode = exitCode;
199
- if (this.status === "running") {
200
- this.status = this.abort.signal.aborted ? "killed" : "exited";
201
- } else if (this.abort.signal.aborted && this.status === "spilled") {
202
- this.status = "killed";
203
- }
204
- if (footer) this.#writeLog(Buffer.from(footer.endsWith("\n") ? footer : `${footer}
205
- `));
206
- if (this.#descriptor !== void 0) {
207
- try {
208
- fs.closeSync(this.#descriptor);
209
- } catch {
210
- }
211
- }
212
- this.#descriptor = void 0;
213
- this.#tail = Buffer.alloc(0);
214
- this.#omitted = false;
215
- if (!this.#spill.signal.aborted) this.#spill.abort();
216
- await unlink(this.pidPath).catch(() => void 0);
217
- if (this.logPath) closeScratch(this.#directory);
218
- else {
219
- try {
220
- fs.rmSync(this.#directory, { recursive: true, force: true });
221
- } catch {
222
- }
223
- }
224
- this.onFinish();
225
- }
226
- info() {
227
- return {
228
- id: this.id,
229
- tool: this.tool,
230
- command: this.command,
231
- ...this.pid !== void 0 ? { pid: this.pid } : {},
232
- ...this.logPath ? { logPath: this.logPath } : {},
233
- startedAt: this.startedAt,
234
- ...this.spilledAt !== void 0 ? { spilledAt: this.spilledAt } : {},
235
- ...this.finishedAt !== void 0 ? { finishedAt: this.finishedAt } : {},
236
- status: this.status,
237
- ...this.exitCode !== void 0 ? { exitCode: this.exitCode } : {}
238
- };
239
- }
240
- };
241
- var trackShellOperations = (inner, job, tool) => ({
242
- exec: (command, cwd, options) => inner.exec(wrapShellCommandForPid(command, job.pidPath, tool), cwd, {
243
- ...options,
244
- onData: (data) => {
245
- job.append(data);
246
- options.onData(data);
247
- }
248
- })
249
- });
250
- var FabricShellJobStore = class {
251
- constructor(tempRoot = tmpdir()) {
252
- this.tempRoot = tempRoot;
253
- }
254
- #jobs = /* @__PURE__ */ new Map();
255
- #prune() {
256
- const completed = [...this.#jobs.values()].filter((job) => job.finished);
257
- completed.sort((a, b) => (a.finishedAt ?? 0) - (b.finishedAt ?? 0));
258
- for (const [index, job] of completed.entries()) {
259
- if (index < completed.length - SHELL_COMPLETED_HANDLES || Date.now() - (job.finishedAt ?? 0) >= SHELL_COMPLETED_MAX_AGE_MS) this.#jobs.delete(job.id);
260
- }
261
- }
262
- begin(tool, command) {
263
- this.#prune();
264
- const job = new FabricShellJob(tool, command, () => this.#prune(), this.tempRoot);
265
- this.#jobs.set(job.id, job);
266
- return job;
267
- }
268
- get(id) {
269
- this.#prune();
270
- return this.#jobs.get(id);
271
- }
272
- list() {
273
- this.#prune();
274
- return [...this.#jobs.values()].map((job) => job.info());
275
- }
276
- waiting() {
277
- return [...this.#jobs.values()].filter((job) => !job.spilled && !job.finished);
278
- }
279
- live() {
280
- return [...this.#jobs.values()].filter((job) => !job.finished);
281
- }
282
- spillWaiting() {
283
- const jobs = this.waiting();
284
- for (const job of jobs) job.spill();
285
- return jobs.length;
286
- }
287
- killWaiting() {
288
- const jobs = this.waiting();
289
- for (const job of jobs) {
290
- if (!job.abort.signal.aborted) job.abort.abort(new Error("Command aborted"));
291
- }
292
- return jobs.length;
293
- }
294
- async close() {
295
- const live = this.live();
296
- for (const job of live) {
297
- if (!job.abort.signal.aborted) job.abort.abort(new Error("Fabric session ended"));
298
- }
299
- await Promise.allSettled(live.map((job) => job.finish(null, "\n\n[Process ended: session closed]\n")));
300
- this.#jobs.clear();
301
- }
302
- };
303
- var raceShellHang = async (options) => {
304
- const { job, parentSignal, hangMs } = options;
305
- const onParentAbort = () => {
306
- if (job.spilled || job.finished || job.abort.signal.aborted) return;
307
- job.abort.abort(parentSignal?.reason ?? new Error("Command aborted"));
308
- };
309
- if (parentSignal) {
310
- if (parentSignal.aborted) onParentAbort();
311
- else parentSignal.addEventListener("abort", onParentAbort, { once: true });
312
- }
313
- const detachParent = () => {
314
- parentSignal?.removeEventListener("abort", onParentAbort);
315
- };
316
- let hangTimer;
317
- const hang = new Promise((resolve) => {
318
- const finish = () => resolve("spill");
319
- if (hangMs > 0) {
320
- hangTimer = setTimeout(() => {
321
- job.spill();
322
- finish();
323
- }, hangMs);
324
- hangTimer.unref?.();
325
- }
326
- if (options.immediate) {
327
- void job.readPid().then(() => {
328
- if (!job.finished) job.spill();
329
- });
330
- }
331
- void job.whenSpill().then(finish);
332
- });
333
- const execute = options.execute(job.abort.signal).then(
334
- (value) => ({ status: "done", value }),
335
- (error) => ({ status: "error", error })
336
- );
337
- try {
338
- const first = await Promise.race([execute, hang]);
339
- if (first === "spill") {
340
- if (job.finished) return execute;
341
- job.spill();
342
- detachParent();
343
- void execute.then(async (result) => {
344
- if (job.finished) return;
345
- if (result.status === "done") {
346
- await job.finish(0, "\n\n[Process exited with code 0]\n");
347
- return;
348
- }
349
- const message = result.error instanceof Error ? result.error.message : String(result.error);
350
- await job.finish(null, "\n\n[" + message + "]\n");
351
- });
352
- return { status: "spilled" };
353
- }
354
- detachParent();
355
- return first;
356
- } finally {
357
- if (hangTimer) clearTimeout(hangTimer);
358
- }
359
- };
360
29
 
361
30
  // src/config.ts
362
- import fs3 from "node:fs";
31
+ import fs2 from "node:fs";
363
32
  import os from "node:os";
364
- import path3 from "node:path";
33
+ import path2 from "node:path";
365
34
 
366
35
  // src/core/damaged-file.ts
367
- import fs2 from "node:fs";
368
- import path2 from "node:path";
36
+ import fs from "node:fs";
37
+ import path from "node:path";
369
38
  var quarantineDamagedFile = (filePath) => {
370
- const destination = path2.join(
371
- path2.dirname(filePath),
372
- `${path2.basename(filePath)}.damaged.${process.pid}.${Date.now()}`
39
+ const destination = path.join(
40
+ path.dirname(filePath),
41
+ `${path.basename(filePath)}.damaged.${process.pid}.${Date.now()}`
373
42
  );
374
43
  try {
375
- fs2.renameSync(filePath, destination);
44
+ fs.renameSync(filePath, destination);
376
45
  return destination;
377
46
  } catch {
378
47
  return void 0;
@@ -696,7 +365,7 @@ var DEFAULT_FABRIC_CONFIG = {
696
365
  var readJsonObjectFile = (filePath) => {
697
366
  let source;
698
367
  try {
699
- source = fs3.readFileSync(filePath, "utf8");
368
+ source = fs2.readFileSync(filePath, "utf8");
700
369
  } catch (error) {
701
370
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
702
371
  return void 0;
@@ -1360,30 +1029,30 @@ var planConfigFile = (filePath) => {
1360
1029
  };
1361
1030
  };
1362
1031
  var writeJsonAtomic = (filePath, document, expectedSource) => {
1363
- const resolvedPath = fs3.existsSync(filePath) ? fs3.realpathSync(filePath) : filePath;
1364
- const directory = path3.dirname(resolvedPath);
1365
- if (!fs3.existsSync(directory)) fs3.mkdirSync(directory, { recursive: true });
1366
- const mode = fs3.existsSync(resolvedPath) ? fs3.statSync(resolvedPath).mode & 511 : 384;
1367
- const temporaryPath = path3.join(
1032
+ const resolvedPath = fs2.existsSync(filePath) ? fs2.realpathSync(filePath) : filePath;
1033
+ const directory = path2.dirname(resolvedPath);
1034
+ if (!fs2.existsSync(directory)) fs2.mkdirSync(directory, { recursive: true });
1035
+ const mode = fs2.existsSync(resolvedPath) ? fs2.statSync(resolvedPath).mode & 511 : 384;
1036
+ const temporaryPath = path2.join(
1368
1037
  directory,
1369
- `.${path3.basename(resolvedPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
1038
+ `.${path2.basename(resolvedPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
1370
1039
  );
1371
1040
  let descriptor;
1372
1041
  try {
1373
- descriptor = fs3.openSync(temporaryPath, "wx", mode);
1374
- fs3.writeFileSync(descriptor, `${JSON.stringify(document, null, 2)}
1042
+ descriptor = fs2.openSync(temporaryPath, "wx", mode);
1043
+ fs2.writeFileSync(descriptor, `${JSON.stringify(document, null, 2)}
1375
1044
  `, "utf8");
1376
- fs3.fsyncSync(descriptor);
1377
- fs3.closeSync(descriptor);
1045
+ fs2.fsyncSync(descriptor);
1046
+ fs2.closeSync(descriptor);
1378
1047
  descriptor = void 0;
1379
1048
  if (expectedSource === null) {
1380
- if (fs3.existsSync(resolvedPath)) {
1049
+ if (fs2.existsSync(resolvedPath)) {
1381
1050
  throw new Error(`Fabric configuration changed while updating ${filePath}`);
1382
1051
  }
1383
1052
  } else {
1384
1053
  let currentSource;
1385
1054
  try {
1386
- currentSource = fs3.readFileSync(resolvedPath, "utf8");
1055
+ currentSource = fs2.readFileSync(resolvedPath, "utf8");
1387
1056
  } catch (error) {
1388
1057
  throw new Error(`Fabric configuration changed while updating ${filePath}`, { cause: error });
1389
1058
  }
@@ -1393,27 +1062,27 @@ var writeJsonAtomic = (filePath, document, expectedSource) => {
1393
1062
  }
1394
1063
  renameAtomic(temporaryPath, resolvedPath);
1395
1064
  try {
1396
- const directoryDescriptor = fs3.openSync(directory, "r");
1065
+ const directoryDescriptor = fs2.openSync(directory, "r");
1397
1066
  try {
1398
- fs3.fsyncSync(directoryDescriptor);
1067
+ fs2.fsyncSync(directoryDescriptor);
1399
1068
  } finally {
1400
- fs3.closeSync(directoryDescriptor);
1069
+ fs2.closeSync(directoryDescriptor);
1401
1070
  }
1402
1071
  } catch (error) {
1403
1072
  const code = error instanceof Error && "code" in error ? error.code : void 0;
1404
1073
  if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EISDIR" && code !== "EPERM") throw error;
1405
1074
  }
1406
1075
  } catch (error) {
1407
- if (descriptor !== void 0) fs3.closeSync(descriptor);
1408
- fs3.rmSync(temporaryPath, { force: true });
1076
+ if (descriptor !== void 0) fs2.closeSync(descriptor);
1077
+ fs2.rmSync(temporaryPath, { force: true });
1409
1078
  throw error;
1410
1079
  }
1411
1080
  };
1412
1081
  var resolveFabricConfig = (options, includeProject, applyEnvironmentOverrides) => {
1413
1082
  let merged = structuredClone(DEFAULT_FABRIC_CONFIG);
1414
1083
  const plans = [
1415
- planConfigFile(path3.join(options.agentDir, "fabric.json")),
1416
- ...includeProject ? [planConfigFile(path3.join(options.cwd, ".pi", "fabric.json"))] : []
1084
+ planConfigFile(path2.join(options.agentDir, "fabric.json")),
1085
+ ...includeProject ? [planConfigFile(path2.join(options.cwd, ".pi", "fabric.json"))] : []
1417
1086
  ].filter((plan) => plan !== void 0);
1418
1087
  for (const plan of plans) {
1419
1088
  if (plan.changed) writeJsonAtomic(plan.path, plan.document, plan.source);
@@ -1459,7 +1128,7 @@ var saveFabricConfig = (options, partial) => {
1459
1128
  if (scope === "project" && !options.projectTrusted) {
1460
1129
  throw new Error("Cannot save project Fabric configuration for an untrusted project");
1461
1130
  }
1462
- const targetPath = scope === "project" ? path3.join(options.cwd, ".pi", "fabric.json") : path3.join(options.agentDir, "fabric.json");
1131
+ const targetPath = scope === "project" ? path2.join(options.cwd, ".pi", "fabric.json") : path2.join(options.agentDir, "fabric.json");
1463
1132
  if (Object.hasOwn(partial, "configVersion") || Object.hasOwn(partial, "subagents")) {
1464
1133
  throw new Error("Fabric configuration updates must use the current schema");
1465
1134
  }
@@ -1476,11 +1145,6 @@ var saveFabricConfig = (options, partial) => {
1476
1145
 
1477
1146
  export {
1478
1147
  DEFAULT_SHELL_HANG_MS,
1479
- formatShellHangNotice,
1480
- appendShellHangNotice,
1481
- trackShellOperations,
1482
- FabricShellJobStore,
1483
- raceShellHang,
1484
1148
  MIN_COMPACTION_TOKEN_THRESHOLD,
1485
1149
  MAX_COMPACTION_TOKEN_THRESHOLD,
1486
1150
  MIN_COMPACTION_RATIO_THRESHOLD,
@@ -1499,4 +1163,4 @@ export {
1499
1163
  loadFabricConfig,
1500
1164
  saveFabricConfig
1501
1165
  };
1502
- //# sourceMappingURL=chunk-H6RSDEWW.js.map
1166
+ //# sourceMappingURL=chunk-F72MAIQY.js.map