frizz-server 0.14.6 → 0.15.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 (94) hide show
  1. package/dist/acp-daemon.js +407 -0
  2. package/dist/claude-agent-broker.js +11 -0
  3. package/dist/dev-child.js +27009 -2607
  4. package/package.json +1 -1
  5. package/web-dist/assets/{TerminalPane-BbBcnZ23.js → TerminalPane-ChkS6RgS.js} +1 -1
  6. package/web-dist/assets/{abnfDiagram-VRR7QNED-B7Q1MCaG.js → abnfDiagram-VRR7QNED-CZEP8flc.js} +1 -1
  7. package/web-dist/assets/architecture-TIHT7OUA-D3wgsRS5.js +1 -0
  8. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-U_o3HwKX.js → architectureDiagram-ZJ3FMSHR-BEXzrjrX.js} +1 -1
  9. package/web-dist/assets/{blockDiagram-677ZJIJ3-BVtitIzJ.js → blockDiagram-677ZJIJ3-CsJ7cQv7.js} +1 -1
  10. package/web-dist/assets/{c4Diagram-LMCZKHZV-BS7Qpf8q.js → c4Diagram-LMCZKHZV-BueHBn6U.js} +1 -1
  11. package/web-dist/assets/channel-DCxTKTH_.js +1 -0
  12. package/web-dist/assets/{chunk-32BRIVSS-Cvq2fN6D.js → chunk-32BRIVSS-DxkYbkXg.js} +1 -1
  13. package/web-dist/assets/{chunk-52WLFC77-VeXenKTG.js → chunk-52WLFC77-B4RUR7JZ.js} +1 -1
  14. package/web-dist/assets/{chunk-C7G6YPKG-FJMI15e9.js → chunk-C7G6YPKG-BLRgwvy4.js} +1 -1
  15. package/web-dist/assets/{chunk-EX3LRPZG-zxGy4vd7.js → chunk-EX3LRPZG-BAtxNbJC.js} +1 -1
  16. package/web-dist/assets/{chunk-FWX5IMBZ-wyhG39cd.js → chunk-FWX5IMBZ-BSC2st3T.js} +2 -2
  17. package/web-dist/assets/{chunk-HOUHSVGY-CzgfjFo5.js → chunk-HOUHSVGY-0dpVw62j.js} +1 -1
  18. package/web-dist/assets/{chunk-ICXQ74PX-zI2FTOFt.js → chunk-ICXQ74PX-BZZPAhRZ.js} +1 -1
  19. package/web-dist/assets/{chunk-MOJQB5TN-BDoDmaWa.js → chunk-MOJQB5TN-ooRCpdkt.js} +1 -1
  20. package/web-dist/assets/{chunk-OGEWGWER-SslhMwtr.js → chunk-OGEWGWER-Ah-NEmel.js} +1 -1
  21. package/web-dist/assets/{chunk-PUDLZKDR-GGjXXaFh.js → chunk-PUDLZKDR-LtHfx3UY.js} +1 -1
  22. package/web-dist/assets/{chunk-Q4XR5HBZ-BTPa3B0G.js → chunk-Q4XR5HBZ-Bd3maQzn.js} +1 -1
  23. package/web-dist/assets/{chunk-V7JOEXUC-D6G1XcaO.js → chunk-V7JOEXUC-Baak7qeE.js} +1 -1
  24. package/web-dist/assets/{chunk-VAUOI2AC-B5V7_Y8l.js → chunk-VAUOI2AC-DmUeRv29.js} +1 -1
  25. package/web-dist/assets/{chunk-VR4S4FIN-DsGWvDSG.js → chunk-VR4S4FIN-BpgSzLFF.js} +1 -1
  26. package/web-dist/assets/{chunk-WYO6CB5R-BY_8sfg6.js → chunk-WYO6CB5R-PnbowZ3i.js} +1 -1
  27. package/web-dist/assets/{chunk-ZGVPDNZ5-BBoYc_fe.js → chunk-ZGVPDNZ5-D5-YXqW9.js} +1 -1
  28. package/web-dist/assets/classDiagram-OUVF2IWQ-DWiNurp2.js +1 -0
  29. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DWiNurp2.js +1 -0
  30. package/web-dist/assets/{cynefin-VYW2F7L2-CPup9cZx.js → cynefin-VYW2F7L2-8VyxZobI.js} +1 -1
  31. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-Cl3irVoi.js → cynefinDiagram-TSTJHNR4-yFUBmk3y.js} +1 -1
  32. package/web-dist/assets/{dagre-VKFMJZFB-CaVEsVrh.js → dagre-VKFMJZFB-wUCK5Cyw.js} +1 -1
  33. package/web-dist/assets/{diagram-FQU43EPY-DPf7R028.js → diagram-FQU43EPY-CGzrn40X.js} +1 -1
  34. package/web-dist/assets/{diagram-G47NLZAW-BeOq2gOT.js → diagram-G47NLZAW-DuhlaKXR.js} +1 -1
  35. package/web-dist/assets/{diagram-NH7WQ7WH-DW39nAyh.js → diagram-NH7WQ7WH-CryBrnG8.js} +1 -1
  36. package/web-dist/assets/{diagram-OA4YK3LP-DSRy0QBE.js → diagram-OA4YK3LP-C_6Ugdyv.js} +1 -1
  37. package/web-dist/assets/{diagram-WEI45ONY-DBZZ9-gh.js → diagram-WEI45ONY-CklWH5cu.js} +1 -1
  38. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-BQhqbJ08.js → ebnfDiagram-CCIWWBDH-DmMOqNaR.js} +1 -1
  39. package/web-dist/assets/{erDiagram-Q63AITRT-YWFordnS.js → erDiagram-Q63AITRT-DBpmNhHP.js} +1 -1
  40. package/web-dist/assets/eventmodeling-45OFAUF4-CRp1CVTM.js +1 -0
  41. package/web-dist/assets/flowDiagram-23GEKE2U-lZUkMIKX.js +1 -0
  42. package/web-dist/assets/{ganttDiagram-NO4QXBWP-C69Iv5AR.js → ganttDiagram-NO4QXBWP-BVFPEPZK.js} +1 -1
  43. package/web-dist/assets/{gitGraph-TEB2WS4Q-CrilrtQw.js → gitGraph-TEB2WS4Q-CdNkzLjA.js} +1 -1
  44. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-DJx5Bqry.js → gitGraphDiagram-IHSO6WYX-BrIGgHoU.js} +1 -1
  45. package/web-dist/assets/index-D5dploZD.js +321 -0
  46. package/web-dist/assets/index-Wees4UUL.css +1 -0
  47. package/web-dist/assets/{info-DKCQHKI2-CJW0Ce93.js → info-DKCQHKI2-BYiyjhUV.js} +1 -1
  48. package/web-dist/assets/{infoDiagram-FWYZ7A6U-BOWnq2zW.js → infoDiagram-FWYZ7A6U-5zjTG5N-.js} +1 -1
  49. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-Biglx1ys.js → ishikawaDiagram-FXEZZL3T-Ca2QU8ga.js} +1 -1
  50. package/web-dist/assets/{journeyDiagram-5HDEW3XC-k3enathS.js → journeyDiagram-5HDEW3XC-CzUGQDiX.js} +1 -1
  51. package/web-dist/assets/{kanban-definition-HUTT4EX6-D8GEpRw0.js → kanban-definition-HUTT4EX6-CjTCAZS0.js} +1 -1
  52. package/web-dist/assets/{line-CHe_pa_l.js → line-uxNpP2NX.js} +1 -1
  53. package/web-dist/assets/{mermaid-parser.core-xAuEpTpK.js → mermaid-parser.core-BADU525L.js} +3 -3
  54. package/web-dist/assets/{mermaid.core-Cblk2suU.js → mermaid.core-OvwlqnDg.js} +3 -3
  55. package/web-dist/assets/{mindmap-definition-LN4V7U3C-zu5mHxT1.js → mindmap-definition-LN4V7U3C-BtbgfcR8.js} +1 -1
  56. package/web-dist/assets/{packet-7NZHBO7P-BzmJzZyb.js → packet-7NZHBO7P-BsfLS3kE.js} +1 -1
  57. package/web-dist/assets/{pegDiagram-2B236MQR-NnlukGZG.js → pegDiagram-2B236MQR-CB5AkYXG.js} +1 -1
  58. package/web-dist/assets/{pie-RZYD4A2V-BSxCIj_U.js → pie-RZYD4A2V-DgnPemgQ.js} +1 -1
  59. package/web-dist/assets/{pieDiagram-ENE6RG2P-DsDCAaSf.js → pieDiagram-ENE6RG2P-DgiTOSOg.js} +1 -1
  60. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-Cs8GeNtT.js → quadrantDiagram-ABIIQ3AL-D56bxzKq.js} +1 -1
  61. package/web-dist/assets/{radar-I7S5WNFK-CWwe4R07.js → radar-I7S5WNFK-Buix85nx.js} +1 -1
  62. package/web-dist/assets/{railroad-3IZDKUUU-D9VKEiaV.js → railroad-3IZDKUUU-_-csUIwQ.js} +1 -1
  63. package/web-dist/assets/railroad-abnf-AHOZXSZD-BGBPTyyT.js +1 -0
  64. package/web-dist/assets/railroad-ebnf-EBAXGLYW-isH8svgX.js +1 -0
  65. package/web-dist/assets/railroad-peg-LSFZ7HO6-DAuLC_Z3.js +1 -0
  66. package/web-dist/assets/{railroadDiagram-RFXS5EU6-BawT2JlG.js → railroadDiagram-RFXS5EU6-C5pjXI43.js} +1 -1
  67. package/web-dist/assets/{requirementDiagram-TGXJPOKE-XDHPtPfx.js → requirementDiagram-TGXJPOKE-DKG82b1A.js} +1 -1
  68. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-dSowfEdK.js → sankeyDiagram-HTMAVEWB-xNsa0WzO.js} +1 -1
  69. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-C__EROVn.js → sequenceDiagram-DBY2YBRQ-CWHvrs9Q.js} +1 -1
  70. package/web-dist/assets/{stateDiagram-2N3HPSRC-Cg1Nddpd.js → stateDiagram-2N3HPSRC-DirRfNzF.js} +1 -1
  71. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-D58IKdqf.js +1 -0
  72. package/web-dist/assets/{swimlanes-5IMT3BWC-DKmvUdu_.js → swimlanes-5IMT3BWC-B0GC66v6.js} +1 -1
  73. package/web-dist/assets/swimlanesDiagram-G3AALYLV-B1vaMvkV.js +8 -0
  74. package/web-dist/assets/{timeline-definition-FHXFAJF6-DL8d36QS.js → timeline-definition-FHXFAJF6-D-UsR0sc.js} +1 -1
  75. package/web-dist/assets/{treeView-QDETBFTQ-BwIXzEk2.js → treeView-QDETBFTQ-BcjBpVxS.js} +1 -1
  76. package/web-dist/assets/{treemap-6X3UGDF4-Cduo02Iv.js → treemap-6X3UGDF4-CrsT6ziM.js} +1 -1
  77. package/web-dist/assets/{vennDiagram-L72KCM5P-DPgx-kOV.js → vennDiagram-L72KCM5P-DrY93og0.js} +1 -1
  78. package/web-dist/assets/{wardley-OPB4EBWU-uOXCOFG6.js → wardley-OPB4EBWU-D1WgLKZM.js} +1 -1
  79. package/web-dist/assets/{wardleyDiagram-EHGQE667-DuqFuXjY.js → wardleyDiagram-EHGQE667-BJ5hU4aG.js} +1 -1
  80. package/web-dist/assets/{xychartDiagram-FW5EYKEG-BI6-eymd.js → xychartDiagram-FW5EYKEG-DS28rhax.js} +1 -1
  81. package/web-dist/index.html +2 -2
  82. package/web-dist/assets/architecture-TIHT7OUA-CQ5OsBvt.js +0 -1
  83. package/web-dist/assets/channel-C8c70Euw.js +0 -1
  84. package/web-dist/assets/classDiagram-OUVF2IWQ-yefBxIWV.js +0 -1
  85. package/web-dist/assets/classDiagram-v2-EOCWNBFH-yefBxIWV.js +0 -1
  86. package/web-dist/assets/eventmodeling-45OFAUF4-h7nF91S2.js +0 -1
  87. package/web-dist/assets/flowDiagram-23GEKE2U-DLwDjSwL.js +0 -1
  88. package/web-dist/assets/index-BfUUFLv7.js +0 -319
  89. package/web-dist/assets/index-D956g6KI.css +0 -1
  90. package/web-dist/assets/railroad-abnf-AHOZXSZD-CtJSUXbE.js +0 -1
  91. package/web-dist/assets/railroad-ebnf-EBAXGLYW-Yc0BdBdp.js +0 -1
  92. package/web-dist/assets/railroad-peg-LSFZ7HO6-Dp0P5Iq6.js +0 -1
  93. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-QKgumchx.js +0 -1
  94. package/web-dist/assets/swimlanesDiagram-G3AALYLV-BSZ6jqLy.js +0 -8
@@ -0,0 +1,407 @@
1
+ import { createRequire as __frizzCreateRequire } from "node:module"; const require = __frizzCreateRequire(import.meta.url);
2
+
3
+ // packages/server/src/backend/acp-daemon.ts
4
+ import { spawn } from "node:child_process";
5
+ import { createServer } from "node:net";
6
+ import { existsSync, readFileSync, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
7
+ import { dirname } from "node:path";
8
+ import { StringDecoder } from "node:string_decoder";
9
+
10
+ // packages/server/src/backend/stale-socket-sweep.ts
11
+ import { execFile } from "node:child_process";
12
+ import { readdirSync, unlinkSync } from "node:fs";
13
+ import { connect as connectSocket } from "node:net";
14
+ import { join } from "node:path";
15
+ function parseLsofSocketNames(stdout) {
16
+ const referenced = /* @__PURE__ */ new Set();
17
+ for (const line of stdout.split("\n")) {
18
+ if (!line.startsWith("n/")) continue;
19
+ const raw = line.slice(1);
20
+ referenced.add(raw);
21
+ const meta = raw.indexOf(" type=");
22
+ if (meta > 0) referenced.add(raw.slice(0, meta));
23
+ }
24
+ return referenced;
25
+ }
26
+ var defaultListReferenced = (prefix, done) => {
27
+ execFile("lsof", ["-U", "-F", "n"], { timeout: 3e4, maxBuffer: 16 * 1024 * 1024 }, (error, stdout) => {
28
+ if (!stdout) return done(null);
29
+ if (error && !stdout.includes(prefix)) return done(null);
30
+ done(parseLsofSocketNames(stdout));
31
+ }).unref?.();
32
+ };
33
+ var defaultProbe = (path, verdict) => {
34
+ const probe = connectSocket(path);
35
+ probe.unref?.();
36
+ probe.on("connect", () => {
37
+ probe.destroy();
38
+ verdict(false);
39
+ });
40
+ probe.on("error", (error) => {
41
+ probe.destroy();
42
+ verdict(error.code === "ECONNREFUSED" || error.code === "ENOENT");
43
+ });
44
+ };
45
+ function sweepStaleSockets(options, deps = {}) {
46
+ if (process.platform === "win32") return;
47
+ const keep = new Set(options.keep ?? []);
48
+ let candidates;
49
+ try {
50
+ candidates = (deps.readdir ?? readdirSync)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join(options.dir, name)).filter((path) => !keep.has(path));
51
+ } catch {
52
+ return;
53
+ }
54
+ if (candidates.length === 0) return;
55
+ const probe = deps.probe ?? defaultProbe;
56
+ const unlink = deps.unlink ?? ((path) => {
57
+ try {
58
+ unlinkSync(path);
59
+ } catch {
60
+ }
61
+ });
62
+ (deps.listReferenced ?? defaultListReferenced)(options.prefix, (referenced) => {
63
+ if (!referenced) return;
64
+ for (const path of candidates) {
65
+ if (referenced.has(path)) continue;
66
+ probe(path, (dead) => {
67
+ if (dead) unlink(path);
68
+ });
69
+ }
70
+ });
71
+ }
72
+
73
+ // packages/server/src/backend/acp-daemon.ts
74
+ var MAX_QUEUED_LINES = 2e4;
75
+ var MAX_QUEUED_BYTES = 64 * 1024 * 1024;
76
+ var MAX_LINE_BYTES = 8 * 1024 * 1024;
77
+ var IDLE_EXIT_MS = 6 * 60 * 60 * 1e3;
78
+ var REACHABILITY_CHECK_MS = 3e4;
79
+ var REACHABILITY_STRIKES = 2;
80
+ function readConfig() {
81
+ const raw = process.env.FRIZZ_ACP_DAEMON;
82
+ if (!raw) throw new Error("acp daemon started without FRIZZ_ACP_DAEMON");
83
+ const config = JSON.parse(raw);
84
+ if (!config.socketPath || !config.recordPath || !config.command || !config.generation || !config.sessionId) {
85
+ throw new Error("acp daemon config is incomplete");
86
+ }
87
+ return config;
88
+ }
89
+ function lineReader(onLine, onOverflow) {
90
+ const decoder = new StringDecoder("utf8");
91
+ let buffer = "";
92
+ let overflowed = false;
93
+ return (chunk) => {
94
+ buffer += decoder.write(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
95
+ for (; ; ) {
96
+ const index = buffer.indexOf("\n");
97
+ if (index < 0) break;
98
+ const line = buffer.slice(0, index);
99
+ buffer = buffer.slice(index + 1);
100
+ if (overflowed) {
101
+ overflowed = false;
102
+ continue;
103
+ }
104
+ if (Buffer.byteLength(line) > MAX_LINE_BYTES) {
105
+ onOverflow();
106
+ continue;
107
+ }
108
+ const trimmed = line.trim();
109
+ if (trimmed) onLine(trimmed);
110
+ }
111
+ if (Buffer.byteLength(buffer) > MAX_LINE_BYTES) {
112
+ buffer = "";
113
+ if (!overflowed) onOverflow();
114
+ overflowed = true;
115
+ }
116
+ };
117
+ }
118
+ function main() {
119
+ const config = readConfig();
120
+ const child = spawn(config.command, config.args, {
121
+ cwd: config.cwd,
122
+ env: config.env,
123
+ stdio: ["pipe", "pipe", "pipe"],
124
+ // This daemon is forked `detached`, which on Windows is DETACHED_PROCESS: it owns NO console, and a
125
+ // console-subsystem agent started without CREATE_NO_WINDOW would get a fresh visible one.
126
+ windowsHide: true
127
+ });
128
+ let client = null;
129
+ let queuedLines = [];
130
+ let queuedBytes = 0;
131
+ let dropped = 0;
132
+ let idleTimer = null;
133
+ let published = false;
134
+ const recordOwner = () => {
135
+ try {
136
+ const value = JSON.parse(readFileSync(config.recordPath, "utf8"));
137
+ return typeof value.daemonPid === "number" ? value.daemonPid : null;
138
+ } catch {
139
+ return null;
140
+ }
141
+ };
142
+ const cleanup = () => {
143
+ try {
144
+ client?.destroy();
145
+ } catch {
146
+ }
147
+ const owner = recordOwner();
148
+ if (owner === process.pid) {
149
+ try {
150
+ unlinkSync2(config.recordPath);
151
+ } catch {
152
+ }
153
+ }
154
+ if (published && owner !== null && owner !== process.pid) return;
155
+ if (published && process.platform !== "win32") {
156
+ try {
157
+ unlinkSync2(config.socketPath);
158
+ } catch {
159
+ }
160
+ }
161
+ };
162
+ const writeExitBreadcrumb = (reason) => {
163
+ try {
164
+ writeFileSync(`${config.recordPath}.exit`, JSON.stringify({
165
+ threadSlug: config.threadSlug,
166
+ sessionId: config.sessionId,
167
+ generation: config.generation,
168
+ daemonPid: process.pid,
169
+ childPid: child.pid ?? null,
170
+ reason,
171
+ at: (/* @__PURE__ */ new Date()).toISOString()
172
+ }));
173
+ } catch {
174
+ }
175
+ };
176
+ const die = (code, reason) => {
177
+ writeExitBreadcrumb(reason);
178
+ cleanup();
179
+ process.exit(code);
180
+ };
181
+ const armIdleExit = () => {
182
+ if (idleTimer) clearTimeout(idleTimer);
183
+ idleTimer = setTimeout(() => {
184
+ try {
185
+ child.kill("SIGTERM");
186
+ } catch {
187
+ }
188
+ ;
189
+ die(0, "idle-timeout");
190
+ }, config.idleExitMs ?? IDLE_EXIT_MS);
191
+ idleTimer.unref?.();
192
+ };
193
+ let unreachableStrikes = 0;
194
+ const checkReachable = () => {
195
+ if (client) {
196
+ unreachableStrikes = 0;
197
+ return;
198
+ }
199
+ if (recordOwner() === process.pid) {
200
+ unreachableStrikes = 0;
201
+ return;
202
+ }
203
+ if (++unreachableStrikes < REACHABILITY_STRIKES) return;
204
+ try {
205
+ child.kill("SIGTERM");
206
+ } catch {
207
+ }
208
+ die(0, "self-collected-record-reassigned");
209
+ };
210
+ const toClient = (line) => {
211
+ if (client) {
212
+ try {
213
+ client.write(`${line}
214
+ `);
215
+ return;
216
+ } catch {
217
+ }
218
+ }
219
+ if (queuedLines.length >= MAX_QUEUED_LINES || queuedBytes >= MAX_QUEUED_BYTES) {
220
+ dropped++;
221
+ return;
222
+ }
223
+ queuedLines.push(line);
224
+ queuedBytes += Buffer.byteLength(line) + 1;
225
+ };
226
+ const control = (payload) => JSON.stringify({ frizz: 1, ...payload });
227
+ const pendingFromClient = /* @__PURE__ */ new Map();
228
+ let nextDaemonId = 1e6;
229
+ let initializeResult = null;
230
+ let initializeDaemonId = null;
231
+ let acpSessionId = null;
232
+ const toChild = (message) => {
233
+ try {
234
+ child.stdin.write(`${JSON.stringify(message)}
235
+ `);
236
+ } catch {
237
+ }
238
+ };
239
+ const readChildLine = (line) => {
240
+ let message;
241
+ try {
242
+ message = JSON.parse(line);
243
+ } catch {
244
+ toClient(line);
245
+ return;
246
+ }
247
+ if (message.id !== void 0 && message.method === void 0 && typeof message.id === "number") {
248
+ const owner = pendingFromClient.get(message.id);
249
+ if (owner !== void 0) {
250
+ pendingFromClient.delete(message.id);
251
+ if (message.id === initializeDaemonId && message.error === void 0) initializeResult = message.result ?? null;
252
+ if (owner.method === "session/new" && message.error === void 0) {
253
+ const result = message.result;
254
+ if (typeof result?.sessionId === "string") acpSessionId = result.sessionId;
255
+ }
256
+ if (owner.method === "session/load" && message.error === void 0) {
257
+ const params = owner.params;
258
+ if (typeof params?.sessionId === "string") acpSessionId = params.sessionId;
259
+ }
260
+ toClient(JSON.stringify({ ...message, id: owner.clientId }));
261
+ return;
262
+ }
263
+ }
264
+ if (message.id !== void 0 && message.method !== void 0) {
265
+ pendingFromChild.set(String(message.id), { line, queued: client === null });
266
+ }
267
+ toClient(line);
268
+ };
269
+ const pendingFromChild = /* @__PURE__ */ new Map();
270
+ const readClientLine = (line) => {
271
+ let message;
272
+ try {
273
+ message = JSON.parse(line);
274
+ } catch {
275
+ return;
276
+ }
277
+ if (message.frizz === 1) {
278
+ const adopt = message.adopt;
279
+ if (adopt && typeof adopt.daemonId === "number" && (typeof adopt.clientId === "number" || typeof adopt.clientId === "string")) {
280
+ const owner = pendingFromClient.get(adopt.daemonId);
281
+ if (owner) owner.clientId = adopt.clientId;
282
+ }
283
+ return;
284
+ }
285
+ if (message.method === "initialize" && initializeResult !== null) {
286
+ if (message.id !== void 0) toClient(JSON.stringify({ jsonrpc: "2.0", id: message.id, result: initializeResult }));
287
+ return;
288
+ }
289
+ if (message.id !== void 0 && message.method !== void 0) {
290
+ const daemonId = nextDaemonId++;
291
+ pendingFromClient.set(daemonId, { clientId: message.id, method: String(message.method), params: message.params });
292
+ if (message.method === "initialize" && initializeDaemonId === null) initializeDaemonId = daemonId;
293
+ toChild({ ...message, id: daemonId });
294
+ return;
295
+ }
296
+ if (message.id !== void 0 && message.method === void 0) pendingFromChild.delete(String(message.id));
297
+ toChild(message);
298
+ };
299
+ const writeRecord = () => {
300
+ writeFileSync(config.recordPath, JSON.stringify({
301
+ threadSlug: config.threadSlug,
302
+ sessionId: config.sessionId,
303
+ generation: config.generation,
304
+ daemonPid: process.pid,
305
+ childPid: child.pid,
306
+ socketPath: config.socketPath,
307
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
308
+ }));
309
+ };
310
+ const server = createServer((sock) => {
311
+ if (client) {
312
+ try {
313
+ client.destroy();
314
+ } catch {
315
+ }
316
+ }
317
+ client = sock;
318
+ if (idleTimer) {
319
+ clearTimeout(idleTimer);
320
+ idleTimer = null;
321
+ }
322
+ sock.setNoDelay(true);
323
+ try {
324
+ sock.write(`${control({
325
+ hello: 1,
326
+ generation: config.generation,
327
+ childPid: child.pid,
328
+ droppedWhileDetached: dropped,
329
+ initialize: initializeResult,
330
+ acpSessionId,
331
+ outstanding: [...pendingFromClient].map(([daemonId, owner]) => ({ daemonId, method: owner.method }))
332
+ })}
333
+ `);
334
+ for (const line of queuedLines) sock.write(`${line}
335
+ `);
336
+ for (const entry of pendingFromChild.values()) {
337
+ if (!entry.queued) sock.write(`${entry.line}
338
+ `);
339
+ entry.queued = false;
340
+ }
341
+ } catch {
342
+ }
343
+ queuedLines = [];
344
+ queuedBytes = 0;
345
+ dropped = 0;
346
+ sock.on("data", lineReader((line) => {
347
+ if (!sock.destroyed) readClientLine(line);
348
+ }, () => sock.destroy()));
349
+ const drop = () => {
350
+ if (client !== sock) return;
351
+ client = null;
352
+ armIdleExit();
353
+ };
354
+ sock.on("close", drop);
355
+ sock.on("error", drop);
356
+ });
357
+ const sweepOwnFamily = () => sweepStaleSockets({ dir: dirname(config.socketPath), prefix: "frizz-acp-", keep: [config.socketPath] });
358
+ const startListening = () => {
359
+ if (process.platform !== "win32" && existsSync(config.socketPath)) {
360
+ try {
361
+ unlinkSync2(config.socketPath);
362
+ } catch {
363
+ }
364
+ }
365
+ server.on("error", () => die(6, "socket-listen-error"));
366
+ server.listen(config.socketPath, () => {
367
+ published = true;
368
+ writeRecord();
369
+ armIdleExit();
370
+ const reachability = setInterval(checkReachable, config.reachabilityCheckMs ?? REACHABILITY_CHECK_MS);
371
+ reachability.unref?.();
372
+ setTimeout(sweepOwnFamily, 5e3).unref?.();
373
+ });
374
+ };
375
+ child.stdout.on("data", lineReader(readChildLine, () => {
376
+ dropped++;
377
+ const lostClient = client;
378
+ client = null;
379
+ lostClient?.destroy();
380
+ armIdleExit();
381
+ }));
382
+ child.stderr.on("data", (chunk) => {
383
+ if (!client) return;
384
+ for (const line of String(chunk).split("\n")) {
385
+ if (!line.trim()) continue;
386
+ try {
387
+ client.write(`${control({ stderr: line.slice(0, 2e3) })}
388
+ `);
389
+ } catch {
390
+ }
391
+ }
392
+ });
393
+ child.on("exit", (code, signal) => die(0, signal ? `agent-killed-${signal}` : `agent-exited-code-${code ?? "null"}`));
394
+ child.on("error", () => die(4, "agent-spawn-error"));
395
+ startListening();
396
+ for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
397
+ process.on(signal, () => {
398
+ try {
399
+ child.kill("SIGTERM");
400
+ } catch {
401
+ }
402
+ ;
403
+ die(0, `signal-${signal}`);
404
+ });
405
+ }
406
+ }
407
+ main();
@@ -24388,6 +24388,17 @@ function startClaudeQuery(executablePath, options) {
24388
24388
  // an additional highest-precedence settings source, layered over `settingSources` above.
24389
24389
  ...claudeEffort.effort ? { effort: claudeEffort.effort } : {},
24390
24390
  ...claudeEffort.ultracode ? { settings: claudeUltracodeSettings() } : {},
24391
+ // AN INTERRUPT MUST NOT KILL THE WORKER'S SUB-AGENTS. The CLI fails closed here: unless the
24392
+ // consumer declares that it renders a per-task stop control wired to `stop_task`, every
24393
+ // interrupt (the ⌘-Enter "interrupt and send", the queued bubble's push-now, a "pause now")
24394
+ // aborts the turn AND kills every background agent under it — because a consumer with no way
24395
+ // to stop a runaway child must not be left with one it cannot end. Frizz HAS that control (the
24396
+ // sub-agent row's stop → `Query.stopTask`, see router.ts / board.ts), so it declares it, and an
24397
+ // interrupt then aborts only the turn: the children keep running and their notifications land
24398
+ // as usual. Measured 2026-09-24 (_live_broker_interrupt_subagent.mts), one variable: the flag
24399
+ // absent → the child was reported `stopped` 7ms after the interrupt; present → it ran
24400
+ // to `completed` after the parent's turn had already been aborted.
24401
+ perTaskStopAffordance: true,
24391
24402
  stderr(data) {
24392
24403
  const redacted = redact(data);
24393
24404
  diagnostic?.({ kind: "stderr", ...redacted });