frizz-server 0.13.8 → 0.14.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 (93) hide show
  1. package/dist/claude-agent-broker.js +14659 -14376
  2. package/dist/dev-child.js +217 -107
  3. package/package.json +1 -1
  4. package/web-dist/assets/{TerminalPane-CgFt4ple.js → TerminalPane-DcNJcvz9.js} +1 -1
  5. package/web-dist/assets/{abnfDiagram-VRR7QNED-DoVWUENi.js → abnfDiagram-VRR7QNED-BFKPkR0J.js} +1 -1
  6. package/web-dist/assets/architecture-TIHT7OUA-3cEHc0mh.js +1 -0
  7. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-BE2vu5s4.js → architectureDiagram-ZJ3FMSHR-BugbGvF7.js} +1 -1
  8. package/web-dist/assets/{blockDiagram-677ZJIJ3-D6LvUZ6S.js → blockDiagram-677ZJIJ3-B1drPHOm.js} +1 -1
  9. package/web-dist/assets/{c4Diagram-LMCZKHZV-BDZel4B4.js → c4Diagram-LMCZKHZV-jA8etf56.js} +1 -1
  10. package/web-dist/assets/channel-BYQQkw21.js +1 -0
  11. package/web-dist/assets/{chunk-32BRIVSS-fmjhgloi.js → chunk-32BRIVSS-BfMUf_Yy.js} +1 -1
  12. package/web-dist/assets/{chunk-52WLFC77-sfh419xE.js → chunk-52WLFC77-Fo52YmmC.js} +1 -1
  13. package/web-dist/assets/{chunk-C7G6YPKG-D-LGx-oa.js → chunk-C7G6YPKG-CPiVGUnC.js} +1 -1
  14. package/web-dist/assets/{chunk-EX3LRPZG-DriDj_NL.js → chunk-EX3LRPZG-DllbzsPn.js} +1 -1
  15. package/web-dist/assets/{chunk-FWX5IMBZ-DetgkllZ.js → chunk-FWX5IMBZ-Cr7SCNOQ.js} +2 -2
  16. package/web-dist/assets/{chunk-HOUHSVGY-CDGGE0PL.js → chunk-HOUHSVGY-D2qjbRMK.js} +1 -1
  17. package/web-dist/assets/{chunk-ICXQ74PX-CIgu37mb.js → chunk-ICXQ74PX-Dl8ZZqX4.js} +1 -1
  18. package/web-dist/assets/{chunk-MOJQB5TN-BNj77oeQ.js → chunk-MOJQB5TN-C7X9x33F.js} +1 -1
  19. package/web-dist/assets/{chunk-OGEWGWER-BRoVc26C.js → chunk-OGEWGWER-DrlPoLRT.js} +1 -1
  20. package/web-dist/assets/{chunk-PUDLZKDR-B9bHTS14.js → chunk-PUDLZKDR-C1WS3qVH.js} +1 -1
  21. package/web-dist/assets/{chunk-Q4XR5HBZ-CjqwAtw6.js → chunk-Q4XR5HBZ-CNhqnN4i.js} +1 -1
  22. package/web-dist/assets/{chunk-V7JOEXUC-ClYL5Jo9.js → chunk-V7JOEXUC-BQsiP60Y.js} +1 -1
  23. package/web-dist/assets/{chunk-VAUOI2AC-DsyULg7-.js → chunk-VAUOI2AC-DeQswo6G.js} +1 -1
  24. package/web-dist/assets/{chunk-VR4S4FIN-x_8ywB7J.js → chunk-VR4S4FIN-QCzpzy3F.js} +1 -1
  25. package/web-dist/assets/{chunk-WYO6CB5R-BDWUsOuN.js → chunk-WYO6CB5R-DYEAXxaM.js} +1 -1
  26. package/web-dist/assets/{chunk-ZGVPDNZ5-wAbPuGaE.js → chunk-ZGVPDNZ5-BRwJsVqD.js} +1 -1
  27. package/web-dist/assets/classDiagram-OUVF2IWQ-BgM58S9B.js +1 -0
  28. package/web-dist/assets/classDiagram-v2-EOCWNBFH-BgM58S9B.js +1 -0
  29. package/web-dist/assets/{cynefin-VYW2F7L2-DLLNSytS.js → cynefin-VYW2F7L2-DFY4wScy.js} +1 -1
  30. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-6kBJrScm.js → cynefinDiagram-TSTJHNR4-BFd6vF05.js} +1 -1
  31. package/web-dist/assets/{dagre-VKFMJZFB-Z_OVqFBK.js → dagre-VKFMJZFB-BTSZ-kNz.js} +1 -1
  32. package/web-dist/assets/{diagram-FQU43EPY-C_nG5FUG.js → diagram-FQU43EPY-CM5L_Teo.js} +1 -1
  33. package/web-dist/assets/{diagram-G47NLZAW-CYQTveO9.js → diagram-G47NLZAW-DMEMjhev.js} +1 -1
  34. package/web-dist/assets/{diagram-NH7WQ7WH-C2DkBAm5.js → diagram-NH7WQ7WH-BINXDe2x.js} +1 -1
  35. package/web-dist/assets/{diagram-OA4YK3LP-CSDwzhMR.js → diagram-OA4YK3LP-DhKBXS5G.js} +1 -1
  36. package/web-dist/assets/{diagram-WEI45ONY-R1MvefNj.js → diagram-WEI45ONY-QaICPLa1.js} +1 -1
  37. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-DBXUjfz2.js → ebnfDiagram-CCIWWBDH-DDQ6NAEt.js} +1 -1
  38. package/web-dist/assets/{erDiagram-Q63AITRT-D2XH_vxG.js → erDiagram-Q63AITRT-eftnFvxT.js} +1 -1
  39. package/web-dist/assets/eventmodeling-45OFAUF4-KZNoVoEu.js +1 -0
  40. package/web-dist/assets/flowDiagram-23GEKE2U-m4sDfmpP.js +1 -0
  41. package/web-dist/assets/{ganttDiagram-NO4QXBWP-BqJqGmJu.js → ganttDiagram-NO4QXBWP-CBUUxbvO.js} +1 -1
  42. package/web-dist/assets/{gitGraph-TEB2WS4Q-BpED4Y4w.js → gitGraph-TEB2WS4Q-B4j6N6bC.js} +1 -1
  43. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-CpCzUYgA.js → gitGraphDiagram-IHSO6WYX-mrIlQ6c3.js} +1 -1
  44. package/web-dist/assets/index-BI0k4RW8.js +319 -0
  45. package/web-dist/assets/index-CzN-tCOl.css +1 -0
  46. package/web-dist/assets/{info-DKCQHKI2-Bm1fWKNN.js → info-DKCQHKI2-Dj4G-7Y8.js} +1 -1
  47. package/web-dist/assets/{infoDiagram-FWYZ7A6U-C61BGKNf.js → infoDiagram-FWYZ7A6U-Baz07f6h.js} +1 -1
  48. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-0Fmof1Bc.js → ishikawaDiagram-FXEZZL3T-C4og8CF4.js} +1 -1
  49. package/web-dist/assets/{journeyDiagram-5HDEW3XC-DHuJoS_U.js → journeyDiagram-5HDEW3XC-B0WV30vH.js} +1 -1
  50. package/web-dist/assets/{kanban-definition-HUTT4EX6-ClLsOpPq.js → kanban-definition-HUTT4EX6-BgDiY6B1.js} +1 -1
  51. package/web-dist/assets/{line-BQNlaa-L.js → line-D0SxxXfr.js} +1 -1
  52. package/web-dist/assets/{mermaid-parser.core-Dhg0FCF0.js → mermaid-parser.core-CWj7pjCH.js} +3 -3
  53. package/web-dist/assets/{mermaid.core-OSL7u28D.js → mermaid.core-Dpi4Z-WK.js} +3 -3
  54. package/web-dist/assets/{mindmap-definition-LN4V7U3C-CMh2RRdG.js → mindmap-definition-LN4V7U3C-DLmQQ7LC.js} +1 -1
  55. package/web-dist/assets/{packet-7NZHBO7P-DqizytaZ.js → packet-7NZHBO7P-T7pVIfFz.js} +1 -1
  56. package/web-dist/assets/{pegDiagram-2B236MQR-C_xsQ7ey.js → pegDiagram-2B236MQR-BvVxzZmj.js} +1 -1
  57. package/web-dist/assets/{pie-RZYD4A2V-Dg0NeJ6P.js → pie-RZYD4A2V-BGz-hjIz.js} +1 -1
  58. package/web-dist/assets/{pieDiagram-ENE6RG2P-B2przwvg.js → pieDiagram-ENE6RG2P-pdOHyNTd.js} +1 -1
  59. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-B5xLDvCk.js → quadrantDiagram-ABIIQ3AL-fLy3coW3.js} +1 -1
  60. package/web-dist/assets/{radar-I7S5WNFK-DItULivd.js → radar-I7S5WNFK-C-VKX4OE.js} +1 -1
  61. package/web-dist/assets/{railroad-3IZDKUUU-BVSgw6fD.js → railroad-3IZDKUUU-BwC-mtji.js} +1 -1
  62. package/web-dist/assets/railroad-abnf-AHOZXSZD-CNsCN0k6.js +1 -0
  63. package/web-dist/assets/railroad-ebnf-EBAXGLYW-Bjabtkan.js +1 -0
  64. package/web-dist/assets/railroad-peg-LSFZ7HO6-DjPlt8Lc.js +1 -0
  65. package/web-dist/assets/{railroadDiagram-RFXS5EU6-DQh-iy6H.js → railroadDiagram-RFXS5EU6-BxPp9neb.js} +1 -1
  66. package/web-dist/assets/{requirementDiagram-TGXJPOKE-B2bjpE-s.js → requirementDiagram-TGXJPOKE-P_BvOjKp.js} +1 -1
  67. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-C-vr6CRd.js → sankeyDiagram-HTMAVEWB-DUv1nZCv.js} +1 -1
  68. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-C48rDu8k.js → sequenceDiagram-DBY2YBRQ-DWNx793M.js} +1 -1
  69. package/web-dist/assets/{stateDiagram-2N3HPSRC-BEHBNPlt.js → stateDiagram-2N3HPSRC-CaOUtkvy.js} +1 -1
  70. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-CbgDxwEv.js +1 -0
  71. package/web-dist/assets/{swimlanes-5IMT3BWC-BK0pUXUO.js → swimlanes-5IMT3BWC-D764GMah.js} +1 -1
  72. package/web-dist/assets/swimlanesDiagram-G3AALYLV-QPzvJba6.js +8 -0
  73. package/web-dist/assets/{timeline-definition-FHXFAJF6-CQ3rH4CA.js → timeline-definition-FHXFAJF6-DeKz_jJG.js} +1 -1
  74. package/web-dist/assets/{treeView-QDETBFTQ-DrgwDaib.js → treeView-QDETBFTQ-D3UnDOk3.js} +1 -1
  75. package/web-dist/assets/{treemap-6X3UGDF4-CGlOb_mN.js → treemap-6X3UGDF4-VItEAzU3.js} +1 -1
  76. package/web-dist/assets/{vennDiagram-L72KCM5P-BB9TjfQX.js → vennDiagram-L72KCM5P-DuzNjZFZ.js} +1 -1
  77. package/web-dist/assets/{wardley-OPB4EBWU-C8EK8ow0.js → wardley-OPB4EBWU-B1uIoPVb.js} +1 -1
  78. package/web-dist/assets/{wardleyDiagram-EHGQE667-CUeOWZBB.js → wardleyDiagram-EHGQE667-H60TjU5y.js} +1 -1
  79. package/web-dist/assets/{xychartDiagram-FW5EYKEG-8ZymnyMt.js → xychartDiagram-FW5EYKEG-BqTqYEwv.js} +1 -1
  80. package/web-dist/index.html +2 -2
  81. package/web-dist/assets/architecture-TIHT7OUA-DJ_aT0bS.js +0 -1
  82. package/web-dist/assets/channel-D3v1O4Wy.js +0 -1
  83. package/web-dist/assets/classDiagram-OUVF2IWQ-LvSRVuLl.js +0 -1
  84. package/web-dist/assets/classDiagram-v2-EOCWNBFH-LvSRVuLl.js +0 -1
  85. package/web-dist/assets/eventmodeling-45OFAUF4-DQtNFbyn.js +0 -1
  86. package/web-dist/assets/flowDiagram-23GEKE2U-DPAGYT4s.js +0 -1
  87. package/web-dist/assets/index-DfJaB5hp.css +0 -1
  88. package/web-dist/assets/index-vTgjYcOv.js +0 -319
  89. package/web-dist/assets/railroad-abnf-AHOZXSZD-Du1T7UC_.js +0 -1
  90. package/web-dist/assets/railroad-ebnf-EBAXGLYW-Ck5QAc8a.js +0 -1
  91. package/web-dist/assets/railroad-peg-LSFZ7HO6-CA1OLi4_.js +0 -1
  92. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-BiZG09Uh.js +0 -1
  93. package/web-dist/assets/swimlanesDiagram-G3AALYLV-CenRab4t.js +0 -8
package/dist/dev-child.js CHANGED
@@ -15290,7 +15290,9 @@ var init_src = __esm({
15290
15290
  SetThreadProfileInput = external_exports.object({
15291
15291
  slug: ThreadSlug,
15292
15292
  model: external_exports.string().trim().min(1).max(200),
15293
- effort: external_exports.string().trim().min(1).max(100)
15293
+ // Absent ONLY for an ACP thread, whose model slug (`acp:<agent>@<model>`) carries no effort axis; the
15294
+ // router refuses a Claude/Codex profile without one rather than storing "".
15295
+ effort: external_exports.string().trim().min(1).max(100).optional()
15294
15296
  }).strict();
15295
15297
  SetThreadProfileResult = external_exports.object({
15296
15298
  effect: external_exports.enum(["applied", "next-resume"])
@@ -16139,6 +16141,39 @@ function resolveProjectIcon(id, detect, options = {}) {
16139
16141
  }, home);
16140
16142
  return found;
16141
16143
  }
16144
+ function renameProject(id, update, home = homedir4()) {
16145
+ const registry = readRegistry(home);
16146
+ const entry = registry.projects.find((p) => p.id === id);
16147
+ if (!entry) return void 0;
16148
+ if (update.slug !== void 0) {
16149
+ const slug = slugifyProject(update.slug);
16150
+ if (RESERVED.has(slug)) throw new Error(`"${slug}" is reserved by Frizz itself`);
16151
+ if (registry.projects.some((p) => p.id !== id && p.slug === slug)) {
16152
+ throw new Error(`another project already uses "${slug}"`);
16153
+ }
16154
+ entry.slug = slug;
16155
+ }
16156
+ if (update.name === null) delete entry.name;
16157
+ else if (update.name !== void 0) entry.name = update.name;
16158
+ if (update.archived !== void 0) entry.archived = update.archived;
16159
+ writeRegistry(registry, home);
16160
+ return entry;
16161
+ }
16162
+ function moveProjectDirectory(id, newBasename, home = homedir4()) {
16163
+ const entry = findById(id, home);
16164
+ if (!entry) throw new Error("No such project.");
16165
+ const name = newBasename.trim();
16166
+ if (!name || name === "." || name === ".." || /[\\/\0]/u.test(name)) {
16167
+ throw new Error("A folder name cannot contain a slash.");
16168
+ }
16169
+ if (!existsSync3(entry.path)) throw new Error(`The folder is missing: ${entry.path}`);
16170
+ const target = join5(dirname4(entry.path), name);
16171
+ if (target === entry.path) return entry;
16172
+ if (existsSync3(target)) throw new Error(`Something already exists at ${target}`);
16173
+ renameSync3(entry.path, target);
16174
+ const moved = registerProject({ dir: target, id }, home);
16175
+ return moved.entry ?? entry;
16176
+ }
16142
16177
  function forgetProject(id, home = homedir4()) {
16143
16178
  const registry = readRegistry(home);
16144
16179
  const before = registry.projects.length;
@@ -26616,13 +26651,13 @@ function searchPath2(env) {
26616
26651
  function vendoredNativeBeside(launcherJs, arch, access) {
26617
26652
  const triple = WINDOWS_TRIPLE[arch];
26618
26653
  if (!triple) return void 0;
26619
- const { dirname: dirname26, join: join49 } = win322;
26620
- const pkgRoot = dirname26(dirname26(launcherJs));
26654
+ const { dirname: dirname27, join: join49 } = win322;
26655
+ const pkgRoot = dirname27(dirname27(launcherJs));
26621
26656
  const platformPkg = `codex-win32-${arch}`;
26622
26657
  const vendorRoots = [
26623
26658
  join49(pkgRoot, "node_modules", "@openai", platformPkg, "vendor"),
26624
26659
  // nested under the package
26625
- join49(dirname26(pkgRoot), platformPkg, "vendor"),
26660
+ join49(dirname27(pkgRoot), platformPkg, "vendor"),
26626
26661
  // hoisted beside it: <prefix>\node_modules\@openai\<pkg>
26627
26662
  join49(pkgRoot, "vendor")
26628
26663
  // the pre-platform-package layout, binaries inside @openai/codex itself
@@ -32451,11 +32486,11 @@ var init_codex_app_server = __esm({
32451
32486
  init_logging();
32452
32487
  CODEX_APP_SERVER_PROVIDER = "codex-app-server";
32453
32488
  CODEX_NATIVE_LISTEN_FLAG = "FRIZZ_CODEX_NATIVE_LISTEN";
32454
- CODEX_APP_SERVER_SUPPORTED_VERSION = "0.154.0";
32489
+ CODEX_APP_SERVER_SUPPORTED_VERSION = "0.155.0";
32455
32490
  CODEX_APP_SERVER_PROTOCOL_REVISION = Object.freeze({
32456
32491
  packageVersion: CODEX_APP_SERVER_SUPPORTED_VERSION,
32457
- sourceTag: "rust-v0.154.0",
32458
- sourceCommit: "6b9826e3aa83b1a5947db50f4332cb9c65f1b340"
32492
+ sourceTag: "rust-v0.155.0",
32493
+ sourceCommit: "f0a1b8f0849d90960bc406b848f32e5a129b0457"
32459
32494
  });
32460
32495
  PROTOCOL_FINGERPRINT = [
32461
32496
  CODEX_APP_SERVER_PROTOCOL_REVISION.sourceTag,
@@ -35204,11 +35239,21 @@ var init_quota = __esm({
35204
35239
  }
35205
35240
  });
35206
35241
 
35242
+ // packages/server/src/local-path.ts
35243
+ function normalizeLocalPath(path3, platform3 = process.platform) {
35244
+ return platform3 === "win32" ? path3.replace(/^\/([a-zA-Z]:[\\/])/, "$1") : path3;
35245
+ }
35246
+ var init_local_path = __esm({
35247
+ "packages/server/src/local-path.ts"() {
35248
+ "use strict";
35249
+ }
35250
+ });
35251
+
35207
35252
  // packages/server/src/local-file.ts
35208
35253
  import { spawn as spawn5 } from "node:child_process";
35209
35254
  import { existsSync as existsSync8, readFileSync as readFileSync18, realpathSync as realpathSync6, statSync as statSync13 } from "node:fs";
35210
35255
  import { homedir as homedir11 } from "node:os";
35211
- import { isAbsolute as isAbsolute4, join as join26, resolve as resolve5, sep as sep2 } from "node:path";
35256
+ import { dirname as dirname10, isAbsolute as isAbsolute4, join as join26, resolve as resolve5, sep as sep2 } from "node:path";
35212
35257
  function isUnder(real, root) {
35213
35258
  let rootReal;
35214
35259
  try {
@@ -35216,9 +35261,25 @@ function isUnder(real, root) {
35216
35261
  } catch {
35217
35262
  return false;
35218
35263
  }
35219
- return real === rootReal || real.startsWith(rootReal.endsWith(sep2) ? rootReal : rootReal + sep2);
35264
+ if (process.platform !== "win32") {
35265
+ return real === rootReal || real.startsWith(rootReal.endsWith(sep2) ? rootReal : rootReal + sep2);
35266
+ }
35267
+ try {
35268
+ const trusted = statSync13(rootReal, { bigint: true });
35269
+ if (!trusted.isDirectory() || trusted.ino === 0n) return false;
35270
+ for (let ancestor = real; ; ) {
35271
+ const actual = statSync13(ancestor, { bigint: true });
35272
+ if (actual.dev === trusted.dev && actual.ino === trusted.ino) return true;
35273
+ const parent = dirname10(ancestor);
35274
+ if (parent === ancestor) return false;
35275
+ ancestor = parent;
35276
+ }
35277
+ } catch {
35278
+ return false;
35279
+ }
35220
35280
  }
35221
35281
  function resolveLocalFile(rawPath, roots) {
35282
+ rawPath = normalizeLocalPath(rawPath);
35222
35283
  if (!isAbsolute4(rawPath)) throw new Error("Local path must be absolute");
35223
35284
  let real;
35224
35285
  try {
@@ -35323,6 +35384,7 @@ var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT, WINDOWS_EDITOR_EXE;
35323
35384
  var init_local_file = __esm({
35324
35385
  "packages/server/src/local-file.ts"() {
35325
35386
  "use strict";
35387
+ init_local_path();
35326
35388
  MARKDOWN_FILE_EXT = /\.(?:md|mdx|markdown)$/i;
35327
35389
  MARKDOWN_READ_LIMIT = 1024 * 1024;
35328
35390
  WINDOWS_EDITOR_EXE = {
@@ -35366,7 +35428,7 @@ var init_thread_links = __esm({
35366
35428
  // packages/server/src/frizz.ts
35367
35429
  import { execFile as execFile6 } from "node:child_process";
35368
35430
  import { lstatSync as lstatSync2, realpathSync as realpathSync7 } from "node:fs";
35369
- import { basename as basename5, dirname as dirname10, join as join27, resolve as resolve6 } from "node:path";
35431
+ import { basename as basename5, dirname as dirname11, join as join27, resolve as resolve6 } from "node:path";
35370
35432
  import { promisify as promisify5 } from "node:util";
35371
35433
  function frizzScriptsDir() {
35372
35434
  if (process.env.FRIZZ_SCRIPTS_DIR) return process.env.FRIZZ_SCRIPTS_DIR;
@@ -35379,7 +35441,7 @@ function directFrizzRoot(projectDir) {
35379
35441
  const stat2 = lstatSync2(path3);
35380
35442
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) return null;
35381
35443
  const real = realpathSync7(path3);
35382
- return dirname10(real) === projectRoot && basename5(real) === ".frizz" ? real : null;
35444
+ return dirname11(real) === projectRoot && basename5(real) === ".frizz" ? real : null;
35383
35445
  } catch {
35384
35446
  return null;
35385
35447
  }
@@ -35655,17 +35717,17 @@ var init_project_mcp_servers = __esm({
35655
35717
 
35656
35718
  // packages/server/src/worker-plugin-dir.ts
35657
35719
  import { existsSync as existsSync9 } from "node:fs";
35658
- import { dirname as dirname11, join as join29 } from "node:path";
35720
+ import { dirname as dirname12, join as join29 } from "node:path";
35659
35721
  import { fileURLToPath as fileURLToPath3 } from "node:url";
35660
35722
  function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
35661
35723
  const override = env.FRIZZ_WORKER_PLUGIN_DIR;
35662
35724
  if (override && existsSync9(join29(override, ".claude-plugin", "plugin.json")))
35663
35725
  return override;
35664
- let current = dirname11(fileURLToPath3(moduleUrl));
35726
+ let current = dirname12(fileURLToPath3(moduleUrl));
35665
35727
  for (; ; ) {
35666
35728
  const candidate = join29(current, "cc-worker");
35667
35729
  if (existsSync9(join29(candidate, ".claude-plugin", "plugin.json"))) return candidate;
35668
- const parent = dirname11(current);
35730
+ const parent = dirname12(current);
35669
35731
  if (parent === current) return void 0;
35670
35732
  current = parent;
35671
35733
  }
@@ -37638,7 +37700,7 @@ var init_claude_agent_broker_bridge = __esm({
37638
37700
 
37639
37701
  // packages/server/src/dispatch.ts
37640
37702
  import { closeSync as closeSync12, constants as constants2, existsSync as existsSync10, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync20, realpathSync as realpathSync8, statSync as statSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync9 } from "node:fs";
37641
- import { basename as basename6, join as join31, dirname as dirname12 } from "node:path";
37703
+ import { basename as basename6, join as join31, dirname as dirname13 } from "node:path";
37642
37704
  import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
37643
37705
  function fallbackTitle(prompt) {
37644
37706
  const firstLine2 = prompt.trim().split("\n", 1)[0].trim();
@@ -37678,12 +37740,12 @@ function resolveLegacyThreadFile(projectDir, value) {
37678
37740
  const frizzStat = lstatSync3(frizzPath);
37679
37741
  if (!frizzStat.isDirectory() || frizzStat.isSymbolicLink()) return null;
37680
37742
  const realFrizz = realpathSync8(frizzPath);
37681
- if (dirname12(realFrizz) !== projectRoot || basename6(realFrizz) !== ".frizz") return null;
37743
+ if (dirname13(realFrizz) !== projectRoot || basename6(realFrizz) !== ".frizz") return null;
37682
37744
  const path3 = join31(realFrizz, `${parsed.data}.md`);
37683
37745
  const before = lstatSync3(path3);
37684
37746
  if (!before.isFile() || before.isSymbolicLink()) return null;
37685
37747
  const realPath = realpathSync8(path3);
37686
- if (dirname12(realPath) !== realFrizz || basename6(realPath) !== `${parsed.data}.md`) return null;
37748
+ if (dirname13(realPath) !== realFrizz || basename6(realPath) !== `${parsed.data}.md`) return null;
37687
37749
  let contents;
37688
37750
  let openedBefore;
37689
37751
  let openedAfter;
@@ -37733,7 +37795,7 @@ function ensureSafeDirectDirectory(parent, name) {
37733
37795
  const stat2 = lstatSync3(path3);
37734
37796
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) throw new Error("unsafe project directory");
37735
37797
  const real = realpathSync8(path3);
37736
- if (dirname12(real) !== parent || basename6(real) !== name) throw new Error("unsafe project directory");
37798
+ if (dirname13(real) !== parent || basename6(real) !== name) throw new Error("unsafe project directory");
37737
37799
  return real;
37738
37800
  }
37739
37801
  function scratchDirRelPath(sessionId) {
@@ -37766,7 +37828,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
37766
37828
  }
37767
37829
  ]
37768
37830
  });
37769
- const bashBackgroundHook = join31(dirname12(hookScript), "bash-background.mjs");
37831
+ const bashBackgroundHook = join31(dirname13(hookScript), "bash-background.mjs");
37770
37832
  return {
37771
37833
  bypass_hook_trust: true,
37772
37834
  hooks: {
@@ -38441,7 +38503,7 @@ import {
38441
38503
  watch as fsWatch
38442
38504
  } from "node:fs";
38443
38505
  import { homedir as homedir14 } from "node:os";
38444
- import { dirname as dirname13, join as join32 } from "node:path";
38506
+ import { dirname as dirname14, join as join32 } from "node:path";
38445
38507
  import watcher from "@parcel/watcher";
38446
38508
  function appServerTurnStalled(liveness, lastActivityAt, nowMs) {
38447
38509
  if (!liveness) return false;
@@ -38490,7 +38552,7 @@ function registeredLegacyFileIsTerminal(projectDir, slug) {
38490
38552
  return status === "done" || status === "dismissed";
38491
38553
  }
38492
38554
  function isBoardRelevantFrizzPath(roots, path3) {
38493
- return path3.endsWith(".md") && roots.has(dirname13(path3));
38555
+ return path3.endsWith(".md") && roots.has(dirname14(path3));
38494
38556
  }
38495
38557
  function futureSnooze(row, nowMs) {
38496
38558
  const parsed = SnoozeUntil.safeParse(row.snoozed_until);
@@ -42307,7 +42369,7 @@ var init_claude = __esm({
42307
42369
 
42308
42370
  // packages/server/src/backend/acp-transcript.ts
42309
42371
  import { appendFileSync as appendFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync14, readFileSync as readFileSync21 } from "node:fs";
42310
- import { dirname as dirname14, join as join34 } from "node:path";
42372
+ import { dirname as dirname15, join as join34 } from "node:path";
42311
42373
  function acpTranscriptPath(stateDir, sessionId) {
42312
42374
  return join34(stateDir, ACP_TRANSCRIPT_DIR, `${sessionId}.jsonl`);
42313
42375
  }
@@ -42486,13 +42548,6 @@ function projectAcpTranscript(raw2, identityPrefix = "acp") {
42486
42548
  }
42487
42549
  return out;
42488
42550
  }
42489
- function readAcpTranscriptFile(absPath, nativeId = absPath) {
42490
- try {
42491
- return projectAcpTranscript(readFileSync21(absPath, "utf8"), `acp:${nativeId}`);
42492
- } catch {
42493
- return [];
42494
- }
42495
- }
42496
42551
  var ACP_TRANSCRIPT_DIR, EVENT_KINDS, AcpTranscriptWriter, TOOL_TEXT_MAX;
42497
42552
  var init_acp_transcript = __esm({
42498
42553
  "packages/server/src/backend/acp-transcript.ts"() {
@@ -42517,7 +42572,7 @@ var init_acp_transcript = __esm({
42517
42572
  AcpTranscriptWriter = class {
42518
42573
  constructor(path3) {
42519
42574
  this.path = path3;
42520
- mkdirSync14(dirname14(path3), { recursive: true });
42575
+ mkdirSync14(dirname15(path3), { recursive: true });
42521
42576
  if (!existsSync12(path3)) appendFileSync3(path3, "");
42522
42577
  }
42523
42578
  append(record) {
@@ -43236,6 +43291,17 @@ var init_acp_bridge = __esm({
43236
43291
  if (live.turn) live.conn.notify("session/cancel", { sessionId: live.acpSessionId });
43237
43292
  void live.conn.close(reason === "shutdown" ? 1e3 : 3e3);
43238
43293
  }
43294
+ /** Switch a LIVE session's model (`session/set_config_option`); `applied: false` when the session is
43295
+ * not open, in which case the caller's stored slug reaches the agent on the next open. A refusal is
43296
+ * written to the transcript as a note, exactly as on open, and still counts as applied: the agent
43297
+ * answered, and the note says what it runs on. */
43298
+ async setModel(threadSlug, sessionId, modelId) {
43299
+ const live = this.sessions.get(sessionId);
43300
+ if (!live || live.slug !== threadSlug || live.exited || live.conn.closed) return { applied: false };
43301
+ const problem = await this.applyModel(live, modelId);
43302
+ if (problem) live.writer.append({ kind: "acp-note", at: this.now(), text: problem });
43303
+ return { applied: true, ...live.model ? { model: live.model } : {} };
43304
+ }
43239
43305
  turnLiveness(threadSlug, sessionId) {
43240
43306
  const live = this.sessions.get(sessionId);
43241
43307
  if (!live || live.slug !== threadSlug) return void 0;
@@ -43763,7 +43829,7 @@ var init_stale_socket_sweep = __esm({
43763
43829
  // packages/server/src/orphan-reaper.ts
43764
43830
  import { execFileSync as execFileSync4 } from "node:child_process";
43765
43831
  import { readFileSync as readFileSync22 } from "node:fs";
43766
- import { dirname as dirname15 } from "node:path";
43832
+ import { dirname as dirname16 } from "node:path";
43767
43833
  function firstTokenBasename(command) {
43768
43834
  const first = command.trimStart().split(/\s+/, 1)[0] ?? "";
43769
43835
  const slash = first.lastIndexOf("/");
@@ -43991,7 +44057,7 @@ function sweepOrphansOnce(deps = {}) {
43991
44057
  }
43992
44058
  function sweepStaleBrokerSockets() {
43993
44059
  try {
43994
- sweepStaleSockets({ dir: dirname15(claudeBrokerSocketPath("", "")), prefix: CLAUDE_BROKER_SOCKET_PREFIX });
44060
+ sweepStaleSockets({ dir: dirname16(claudeBrokerSocketPath("", "")), prefix: CLAUDE_BROKER_SOCKET_PREFIX });
43995
44061
  } catch {
43996
44062
  }
43997
44063
  }
@@ -47435,7 +47501,7 @@ var init_server = __esm({
47435
47501
 
47436
47502
  // packages/server/src/repair.ts
47437
47503
  import { existsSync as existsSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync11 } from "node:fs";
47438
- import { basename as basename7, dirname as dirname16, resolve as resolve8 } from "node:path";
47504
+ import { basename as basename7, dirname as dirname17, resolve as resolve8 } from "node:path";
47439
47505
  function quoteValue(v) {
47440
47506
  if (/^[\w./#:+-]+$/.test(v)) return v;
47441
47507
  return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -47447,7 +47513,7 @@ function deriveTitle(body, slug) {
47447
47513
  function repairThreadFile(frizzDir, file) {
47448
47514
  const root = resolve8(frizzDir);
47449
47515
  const abs = resolve8(root, file);
47450
- if (dirname16(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
47516
+ if (dirname17(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
47451
47517
  if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
47452
47518
  if (!existsSync13(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
47453
47519
  const content = readFileSync23(abs, "utf8");
@@ -49217,6 +49283,17 @@ function parseCodexTranscript(raw2, identityPrefix = "codex") {
49217
49283
  return latestTranscriptWindow(out);
49218
49284
  }
49219
49285
  function latestWindowStart(messages) {
49286
+ return byteBoundedStart(messages, countBoundedStart(messages));
49287
+ }
49288
+ function byteBoundedStart(messages, start) {
49289
+ let bytes = 0;
49290
+ for (let i = messages.length - 1; i >= start; i--) {
49291
+ bytes += messageBytes(messages[i]);
49292
+ if (bytes > LATEST_WINDOW_MAX_BYTES) return Math.min(i + 1, messages.length - 1);
49293
+ }
49294
+ return start;
49295
+ }
49296
+ function countBoundedStart(messages) {
49220
49297
  const tail = Math.max(0, messages.length - MAX_MESSAGES);
49221
49298
  if (tail === 0) return 0;
49222
49299
  let boundary = -1;
@@ -50177,7 +50254,7 @@ function emptyTranscriptPage(source) {
50177
50254
  editedFiles: []
50178
50255
  };
50179
50256
  }
50180
- function readLatestThreadTranscriptPage(project, storage, slug, backendFor) {
50257
+ function readLatestThreadTranscriptPage(project, storage, slug, backendFor, opts = {}) {
50181
50258
  let source = sourceForThread(project, storage, slug, backendFor);
50182
50259
  if (!source) return emptyTranscriptPage();
50183
50260
  let snapshot = fixedSnapshot(source);
@@ -50213,7 +50290,7 @@ function readLatestThreadTranscriptPage(project, storage, slug, backendFor) {
50213
50290
  // ones inside this checkout belong on the rail (edited-files.ts). Then git drops whatever the
50214
50291
  // repository would not carry — scratch, build output — leaving the rail an account of repo work
50215
50292
  // (repo-files.ts).
50216
- editedFiles: repoCarriedEditedFiles(project.dir, editedFilesOf(projected, project.dir))
50293
+ ...opts.editedFiles === false ? {} : { editedFiles: repoCarriedEditedFiles(project.dir, editedFilesOf(projected, project.dir)) }
50217
50294
  };
50218
50295
  }
50219
50296
  function readEarlierThreadTranscriptPage(project, storage, slug, cursor, backendFor) {
@@ -50252,41 +50329,6 @@ function readCodexTranscriptFile(absPath, nativeId = absPath) {
50252
50329
  return [];
50253
50330
  }
50254
50331
  }
50255
- function readThreadTranscript(project, storage, slug, backendFor) {
50256
- const row = storage.getSession(slug);
50257
- if (row) {
50258
- if (row.backend === "acp") {
50259
- const path3 = backendFor?.("acp")?.transcriptPath(row.session_id);
50260
- const acpLedger = parseDeliveryLedger(row.delivery_ledger);
50261
- return projectDeliveryLedger(path3 ? readAcpTranscriptFile(path3, row.session_id) : [], acpLedger);
50262
- }
50263
- if (row.backend === "codex") {
50264
- const backend = backendFor?.("codex") ?? defaultCodexBackend();
50265
- const nativeId = row.agent_session_id ?? row.session_id;
50266
- const path3 = backend.transcriptPath(nativeId);
50267
- const codexLedger = parseDeliveryLedger(row.delivery_ledger);
50268
- return projectDeliveryLedger(path3 ? readCodexTranscriptFile(path3, nativeId) : [], codexLedger);
50269
- }
50270
- const ledger = parseDeliveryLedger(row.delivery_ledger);
50271
- const msgs = readTranscript(project, row.transcript_id ?? row.session_id);
50272
- if (msgs.length || row.transcript_id) return projectDeliveryLedger(msgs, ledger);
50273
- if (Date.now() - Date.parse(row.spawned_at) < DISCOVERY_GRACE_MS) return projectDeliveryLedger(msgs, ledger);
50274
- const exclude = /* @__PURE__ */ new Set();
50275
- for (const r of storage.allSessions()) {
50276
- if (r.slug === row.slug) continue;
50277
- exclude.add(r.session_id);
50278
- if (r.transcript_id) exclude.add(r.transcript_id);
50279
- }
50280
- const found = discoverTranscriptId(logDirOf(project), row.session_id, { exclude });
50281
- return projectDeliveryLedger(found ? readTranscript(project, found) : msgs, ledger);
50282
- }
50283
- if (FOREIGN_SESSION_ID_RE.test(slug)) {
50284
- const source = sourceForThread(project, storage, slug, backendFor);
50285
- if (!source) return [];
50286
- return source.backend === "codex" ? readCodexTranscriptFile(source.path, slug) : readTranscript(project, slug);
50287
- }
50288
- return [];
50289
- }
50290
50332
  function projectTranscriptAgentLifecycles(messages, lookup) {
50291
50333
  const cache2 = /* @__PURE__ */ new Map();
50292
50334
  const projectTool = (tool) => {
@@ -50355,7 +50397,7 @@ function readTranscriptFile(absPath) {
50355
50397
  return [];
50356
50398
  }
50357
50399
  }
50358
- var MAX_MESSAGES, LATEST_WINDOW_ASK_REACH_ITEMS, LATEST_WINDOW_ASK_REACH_BYTES, REST_LABEL, LEGACY_TASK_MARKER, LEGACY_BANNER_PREAMBLE, COMMAND_NAME_RE, COMMAND_ARGS_RE, PEER_SESSION_PREAMBLE, CLAUDE_COORDINATOR_INSTRUCTION_PREFIX, CLAUDE_COORDINATOR_INSTRUCTION_SUFFIX, CLAUDE_PEER_INSTRUCTION_PREFIX, QUEUED_STALE_MS, EDIT_CAP, TRUNC_MARKER, ENCRYPTED_PAYLOAD, AGENT_PROMPT_CAP, SEND_BODY_CAP, COMMAND_CAP, TOOL_INPUT_CAP, READ_LINE_CAP, READ_BYTE_CAP, IMAGE_MEDIA_EXT, SCREENSHOT_CACHE_DIR, SCREENSHOT_CACHE_MAX, SCREENSHOT_MAX_BASE64, screenshotTmpSeq, SENT_IMAGE_MAX_BYTES, SENT_FILES_MAX, TODO_ROWS_MAX, TODO_TEXT_CAP, TRANSCRIPT_CACHE_CAP, transcriptCache, PARSE_VERIFY_SAMPLE, TRANSCRIPT_DECODE_WINDOW, FOREIGN_SESSION_ID_RE, MAX_PINNED_BACKGROUND_OPERATIONS, CAPTION_MAX, _defaultCodexBackend, TRANSCRIPT_EARLIER_MAX_ITEMS, TRANSCRIPT_EARLIER_MAX_BYTES, fullDigestCache, FULL_DIGEST_CACHE_CAP, EMPTY_RETIRED_OPS;
50400
+ var MAX_MESSAGES, LATEST_WINDOW_ASK_REACH_ITEMS, LATEST_WINDOW_ASK_REACH_BYTES, LATEST_WINDOW_MAX_BYTES, REST_LABEL, LEGACY_TASK_MARKER, LEGACY_BANNER_PREAMBLE, COMMAND_NAME_RE, COMMAND_ARGS_RE, PEER_SESSION_PREAMBLE, CLAUDE_COORDINATOR_INSTRUCTION_PREFIX, CLAUDE_COORDINATOR_INSTRUCTION_SUFFIX, CLAUDE_PEER_INSTRUCTION_PREFIX, QUEUED_STALE_MS, EDIT_CAP, TRUNC_MARKER, ENCRYPTED_PAYLOAD, AGENT_PROMPT_CAP, SEND_BODY_CAP, COMMAND_CAP, TOOL_INPUT_CAP, READ_LINE_CAP, READ_BYTE_CAP, IMAGE_MEDIA_EXT, SCREENSHOT_CACHE_DIR, SCREENSHOT_CACHE_MAX, SCREENSHOT_MAX_BASE64, screenshotTmpSeq, SENT_IMAGE_MAX_BYTES, SENT_FILES_MAX, TODO_ROWS_MAX, TODO_TEXT_CAP, TRANSCRIPT_CACHE_CAP, transcriptCache, PARSE_VERIFY_SAMPLE, TRANSCRIPT_DECODE_WINDOW, FOREIGN_SESSION_ID_RE, MAX_PINNED_BACKGROUND_OPERATIONS, CAPTION_MAX, _defaultCodexBackend, TRANSCRIPT_EARLIER_MAX_ITEMS, TRANSCRIPT_EARLIER_MAX_BYTES, fullDigestCache, FULL_DIGEST_CACHE_CAP, EMPTY_RETIRED_OPS;
50359
50401
  var init_transcript = __esm({
50360
50402
  "packages/server/src/transcript.ts"() {
50361
50403
  "use strict";
@@ -50376,6 +50418,7 @@ var init_transcript = __esm({
50376
50418
  MAX_MESSAGES = 300;
50377
50419
  LATEST_WINDOW_ASK_REACH_ITEMS = 100;
50378
50420
  LATEST_WINDOW_ASK_REACH_BYTES = 512 * 1024;
50421
+ LATEST_WINDOW_MAX_BYTES = 3 * 1024 * 1024;
50379
50422
  REST_LABEL = "Agent rested";
50380
50423
  LEGACY_TASK_MARKER = "\nTASK:\n";
50381
50424
  LEGACY_BANNER_PREAMBLE = "Everything ABOVE this line is frizz system orientation. Everything BELOW the `TASK:` marker is the human operator's own prompt, verbatim.";
@@ -51151,7 +51194,7 @@ var init_directory_picker = __esm({
51151
51194
  import { readFileSync as readFileSync26, statSync as statSync19 } from "node:fs";
51152
51195
  import { join as join39, resolve as resolve9 } from "node:path";
51153
51196
  import { randomUUID as randomUUID16 } from "node:crypto";
51154
- import { basename as basename9, dirname as dirname17 } from "node:path";
51197
+ import { basename as basename9, dirname as dirname18 } from "node:path";
51155
51198
  import { existsSync as existsSync15, mkdirSync as mkdirSync17, rmSync as rmSync10, writeFileSync as writeFileSync13 } from "node:fs";
51156
51199
  import { homedir as homedir17 } from "node:os";
51157
51200
  function validateGithubDispatchProfile(input) {
@@ -51351,7 +51394,7 @@ function storeProjectIcon(id, name, bytes) {
51351
51394
  if (!entry) throw new Error("No such project.");
51352
51395
  if (bytes.length === 0) throw new Error("That file is empty.");
51353
51396
  const path3 = customIconPath(id, `.${extension2}`);
51354
- mkdirSync17(dirname17(path3), { recursive: true });
51397
+ mkdirSync17(dirname18(path3), { recursive: true });
51355
51398
  writeFileSync13(path3, bytes);
51356
51399
  if (!imageDimensions(path3)) {
51357
51400
  rmSync10(path3, { force: true });
@@ -52304,7 +52347,16 @@ ${gapNote}` : input.message;
52304
52347
  const thread = (await ctx.board.snapshot()).threads.find((candidate) => candidate.id === input.slug);
52305
52348
  if (!thread || thread.foreign || thread.kind !== "session") throw new Error(`thread ${input.slug} is not editable`);
52306
52349
  const profRow = ctx.storage.getSession(input.slug);
52307
- if (profRow?.backend === "acp") throw new Error("An ACP agent chooses its own model; Frizz offers no profile for it yet");
52350
+ if (profRow?.backend === "acp") {
52351
+ if (acpAgentIdFromModel(input.model) !== acpAgentIdFromModel(profRow.model ?? "")) {
52352
+ throw new Error("An ACP thread's agent cannot change; pick a model of the same agent");
52353
+ }
52354
+ ctx.storage.setProfile(input.slug, input.model, "");
52355
+ const live = ctx.acpBridge ? await ctx.acpBridge.setModel(input.slug, profRow.session_id, acpModelIdFromModel(input.model)) : { applied: false };
52356
+ ctx.board.refresh();
52357
+ return { effect: live.applied ? "applied" : "next-resume" };
52358
+ }
52359
+ if (!input.effort) throw new Error(`effort is required for a ${profRow?.backend ?? "claude"} thread`);
52308
52360
  if (profRow?.backend === "codex") {
52309
52361
  ctx.storage.setProfile(input.slug, input.model, input.effort);
52310
52362
  ctx.board.refresh();
@@ -53448,6 +53500,47 @@ Say which way you went and why in your write-up, so the human can course-correct
53448
53500
  return projectCard(updated, !existsSync15(updated.path));
53449
53501
  }
53450
53502
  }),
53503
+ /**
53504
+ * Rename a project — the name Frizz shows and the URL it answers on, and, only when asked, the
53505
+ * folder itself.
53506
+ *
53507
+ * The name is the registry's display override, cleared again when it matches the folder so the
53508
+ * card keeps following a later `mv`. The slug follows the name on purpose: a rename is the one
53509
+ * time an operator has said what this project is called, and `deriveSlug`'s never-re-derive rule
53510
+ * exists to protect bookmarks from SILENT changes, not from this one. A taken or reserved slug is
53511
+ * refused with the registry's own message, and nothing is written.
53512
+ *
53513
+ * `renameDirectory` is opt-in and the dialog only offers it when the folder is already named after
53514
+ * the project (maintainer 2026-09-18: "without actually changing the directory name by default").
53515
+ * It moves the folder to a sibling of the same name and re-registers the id there, then closes
53516
+ * the tenant so the next request reopens it at the new path — a context built on the old one
53517
+ * would spawn every worker into a directory that no longer exists. The launching project is
53518
+ * refused for the same reason `projectRemove` refuses it: this process is standing in it.
53519
+ */
53520
+ projectRename: mutation({
53521
+ input: external_exports.object({
53522
+ id: external_exports.string().min(1),
53523
+ name: external_exports.string().trim().min(1).max(120),
53524
+ renameDirectory: external_exports.boolean().optional()
53525
+ }).strict(),
53526
+ output: ProjectCard,
53527
+ handler: async ({ input }) => {
53528
+ const entry = findById(input.id);
53529
+ if (!entry) throw new Error("No such project.");
53530
+ const name = input.name.trim();
53531
+ let path3 = entry.path;
53532
+ if (input.renameDirectory && name !== basename9(entry.path)) {
53533
+ if (ctx.launchProjectId === entry.id) {
53534
+ throw new Error("Frizz is serving from this project, so its folder cannot be renamed. Restart Frizz from another folder first.");
53535
+ }
53536
+ path3 = moveProjectDirectory(entry.id, name).path;
53537
+ await ctx.teardownProject?.(entry.id, {});
53538
+ }
53539
+ const updated = renameProject(entry.id, { name: name === basename9(path3) ? null : name, slug: name });
53540
+ if (!updated) throw new Error("No such project.");
53541
+ return projectCard(updated, !existsSync15(updated.path));
53542
+ }
53543
+ }),
53451
53544
  /**
53452
53545
  * Open the machine's own folder picker, and add whatever comes back.
53453
53546
  *
@@ -53865,6 +53958,7 @@ var init_compression = __esm({
53865
53958
  import { readFileSync as readFileSync27, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
53866
53959
  import { extname as extname2, isAbsolute as isAbsolute7 } from "node:path";
53867
53960
  function resolveLocalImage(rawPath) {
53961
+ if (rawPath) rawPath = normalizeLocalPath(rawPath);
53868
53962
  if (!rawPath || !isAbsolute7(rawPath)) return { status: 400 };
53869
53963
  const contentType = IMAGE_CONTENT_TYPE[extname2(rawPath).toLowerCase()];
53870
53964
  if (!contentType) return { status: 400 };
@@ -53885,6 +53979,7 @@ var IMAGE_CONTENT_TYPE;
53885
53979
  var init_local_image = __esm({
53886
53980
  "packages/server/src/local-image.ts"() {
53887
53981
  "use strict";
53982
+ init_local_path();
53888
53983
  IMAGE_CONTENT_TYPE = {
53889
53984
  ".png": "image/png",
53890
53985
  ".jpg": "image/jpeg",
@@ -53898,7 +53993,7 @@ var init_local_image = __esm({
53898
53993
  // packages/server/src/project-icon.ts
53899
53994
  import { readFileSync as readFileSync28, readdirSync as readdirSync10, realpathSync as realpathSync12, statSync as statSync21 } from "node:fs";
53900
53995
  import { homedir as homedir18 } from "node:os";
53901
- import { basename as basename10, dirname as dirname18, extname as extname3, join as join40, relative as relative2, sep as sep3 } from "node:path";
53996
+ import { basename as basename10, dirname as dirname19, extname as extname3, join as join40, relative as relative2, sep as sep3 } from "node:path";
53902
53997
  function normalizeStem(stem) {
53903
53998
  const sized = stem.toLowerCase().replace(/[@-]\d+(\.\d+)?x(\d+)?$/u, "").replace(/[-_]\d{2,4}$/u, "");
53904
53999
  const normalized = sized.replace(VARIANT_SUFFIX, "");
@@ -53963,7 +54058,7 @@ function manifestIcons(manifestPath) {
53963
54058
  return [];
53964
54059
  }
53965
54060
  if (!Array.isArray(parsed?.icons)) return [];
53966
- const directory = dirname18(manifestPath);
54061
+ const directory = dirname19(manifestPath);
53967
54062
  return parsed.icons.map((icon) => typeof icon?.src === "string" ? icon.src : void 0).filter((src) => !!src && !/^(https?:)?\/\//u.test(src) && !src.startsWith("data:")).map((src) => join40(directory, src.replace(/[?#].*$/u, "").replace(/^\//u, "")));
53968
54063
  }
53969
54064
  function projectIconCandidates(root) {
@@ -53986,7 +54081,7 @@ function projectIconCandidates(root) {
53986
54081
  for (const icon of manifestIcons(join40(directory, name))) {
53987
54082
  if (MEASURABLE_IMAGE_EXTENSIONS.has(extname3(icon).toLowerCase())) {
53988
54083
  fromManifest.add(icon);
53989
- files.push({ path: icon, directory: dirname18(icon) });
54084
+ files.push({ path: icon, directory: dirname19(icon) });
53990
54085
  }
53991
54086
  }
53992
54087
  continue;
@@ -54902,10 +54997,19 @@ function isWsPath(url) {
54902
54997
  }
54903
54998
  function makeTranscriptReader(project, storage, backendFor, lifecycleFor, peerNameFor, ownerGone) {
54904
54999
  return (slug) => {
54905
- const messages = readThreadTranscript(project, storage, slug, backendFor);
54906
- const named = peerNameFor ? projectTranscriptPeerNames(messages, (taskId) => peerNameFor(slug, taskId)) : messages;
55000
+ const page = readLatestThreadTranscriptPage(project, storage, slug, backendFor, { editedFiles: false });
55001
+ const named = peerNameFor ? projectTranscriptPeerNames(page.messages, (taskId) => peerNameFor(slug, taskId)) : page.messages;
54907
55002
  const projected = lifecycleFor ? projectTranscriptAgentLifecycles(named, (id) => lifecycleFor(slug, id)) : named;
54908
- return projectRetiredBackgroundOps(projected, retiredOpsFor(storage, slug), ownerGone?.(slug) ?? false);
55003
+ const messages = projectRetiredBackgroundOps(projected, retiredOpsFor(storage, slug), ownerGone?.(slug) ?? false);
55004
+ return {
55005
+ messages,
55006
+ page: {
55007
+ beforeCursor: page.beforeCursor,
55008
+ hasEarlier: page.hasEarlier,
55009
+ reachedTurnBoundary: page.reachedTurnBoundary,
55010
+ transcriptKey: page.transcriptKey
55011
+ }
55012
+ };
54909
55013
  };
54910
55014
  }
54911
55015
  function createAppSocketServer(deps) {
@@ -55186,9 +55290,9 @@ function createAppSocketServer(deps) {
55186
55290
  if (cached2) return { kind: "ready", snapshot: cached2 };
55187
55291
  const reservation = reserveTranscriptRead(origin);
55188
55292
  if (reservation.kind === "limited") return reservation;
55189
- const messages = readTranscript2(slug);
55190
- if (!messages) return { kind: "unavailable" };
55191
- const frame = encodeMsg({ t: "transcript", slug, messages });
55293
+ const read = readTranscript2(slug);
55294
+ if (!read) return { kind: "unavailable" };
55295
+ const frame = encodeMsg(read.page ? { t: "transcript", slug, messages: read.messages, page: read.page } : { t: "transcript", slug, messages: read.messages });
55192
55296
  if (!frame) return { kind: "unavailable" };
55193
55297
  if (frame.bytes > maxLogicalFrameBytes) return { kind: "oversized", actualBytes: frame.bytes };
55194
55298
  const snapshot = { frame, sig: frameSignature(frame), weight: snapshotWeight(frame) };
@@ -55749,7 +55853,7 @@ var init_app_socket = __esm({
55749
55853
 
55750
55854
  // packages/server/src/local-file-watch.ts
55751
55855
  import { statSync as statSync23, watch } from "node:fs";
55752
- import { basename as basename11, dirname as dirname19 } from "node:path";
55856
+ import { basename as basename11, dirname as dirname20 } from "node:path";
55753
55857
  function stampOf(path3) {
55754
55858
  try {
55755
55859
  const s = statSync23(path3, { bigint: true });
@@ -55772,7 +55876,7 @@ function watchLocalFile(path3, onChange) {
55772
55876
  let entry = entries.get(path3);
55773
55877
  if (!entry) {
55774
55878
  const name = basename11(path3);
55775
- const watcher2 = watch(dirname19(path3), { persistent: false }, (_event, changed) => {
55879
+ const watcher2 = watch(dirname20(path3), { persistent: false }, (_event, changed) => {
55776
55880
  if (changed !== null && changed !== void 0 && changed.toString() !== name) return;
55777
55881
  const current = entries.get(path3);
55778
55882
  if (!current) return;
@@ -55809,7 +55913,7 @@ var init_local_file_watch = __esm({
55809
55913
 
55810
55914
  // packages/server/src/boot-progress.ts
55811
55915
  import { mkdirSync as mkdirSync19, readFileSync as readFileSync30, renameSync as renameSync10, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
55812
- import { basename as basename12, dirname as dirname20, join as join43 } from "node:path";
55916
+ import { basename as basename12, dirname as dirname21, join as join43 } from "node:path";
55813
55917
  function bootProgressPath(stateDir) {
55814
55918
  return join43(stateDir, BOOT_PROGRESS_NAME);
55815
55919
  }
@@ -55831,8 +55935,8 @@ function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
55831
55935
  lastAt = now;
55832
55936
  step++;
55833
55937
  try {
55834
- mkdirSync19(dirname20(path3), { recursive: true, mode: 448 });
55835
- const temp = join43(dirname20(path3), `.${basename12(path3)}.${process.pid}.tmp`);
55938
+ mkdirSync19(dirname21(path3), { recursive: true, mode: 448 });
55939
+ const temp = join43(dirname21(path3), `.${basename12(path3)}.${process.pid}.tmp`);
55836
55940
  writeFileSync15(temp, `${JSON.stringify({ pid: process.pid, step, phase, at: new Date(now).toISOString() })}
55837
55941
  `, "utf8");
55838
55942
  renameSync10(temp, path3);
@@ -55859,12 +55963,12 @@ var init_boot_progress = __esm({
55859
55963
 
55860
55964
  // packages/server/src/runtime-lease.ts
55861
55965
  import { existsSync as existsSync16, mkdirSync as mkdirSync20, readdirSync as readdirSync12, readFileSync as readFileSync31, unlinkSync as unlinkSync6, writeFileSync as writeFileSync16 } from "node:fs";
55862
- import { dirname as dirname21, join as join44 } from "node:path";
55966
+ import { dirname as dirname22, join as join44 } from "node:path";
55863
55967
  function runtimeVersionDir(bin) {
55864
- let dir = dirname21(bin);
55968
+ let dir = dirname22(bin);
55865
55969
  for (let step = 0; step < MAX_ASCENT; step++) {
55866
55970
  if (existsSync16(join44(dir, PROVISIONED_MARKER))) return dir;
55867
- const parent = dirname21(dir);
55971
+ const parent = dirname22(dir);
55868
55972
  if (parent === dir) return void 0;
55869
55973
  dir = parent;
55870
55974
  }
@@ -55884,7 +55988,7 @@ function leaseRuntime(bin, role) {
55884
55988
  };
55885
55989
  const path3 = join44(versionDir, LEASES_DIR, `${process.pid}.json`);
55886
55990
  try {
55887
- mkdirSync20(dirname21(path3), { recursive: true });
55991
+ mkdirSync20(dirname22(path3), { recursive: true });
55888
55992
  const lease = { pid: process.pid, role, bin, at: (/* @__PURE__ */ new Date()).toISOString() };
55889
55993
  writeFileSync16(path3, JSON.stringify(lease));
55890
55994
  } catch {
@@ -55954,7 +56058,7 @@ import {
55954
56058
  writeFileSync as writeFileSync17,
55955
56059
  writeSync as writeSync2
55956
56060
  } from "node:fs";
55957
- import { dirname as dirname22, isAbsolute as isAbsolute8, join as join45, normalize, relative as relative3, sep as sep5 } from "node:path";
56061
+ import { dirname as dirname23, isAbsolute as isAbsolute8, join as join45, normalize, relative as relative3, sep as sep5 } from "node:path";
55958
56062
  import { Readable, Transform, Writable as Writable3 } from "node:stream";
55959
56063
  import { pipeline } from "node:stream/promises";
55960
56064
  import { createGunzip } from "node:zlib";
@@ -56215,8 +56319,8 @@ var init_runtimes = __esm({
56215
56319
  init_codex_app_server();
56216
56320
  init_frizz_paths();
56217
56321
  init_runtime_lease();
56218
- CLAUDE_AGENT_SDK_VERSION = "0.3.273";
56219
- CLAUDE_CODE_VERSION = "2.1.273";
56322
+ CLAUDE_AGENT_SDK_VERSION = "0.3.275";
56323
+ CLAUDE_CODE_VERSION = "2.1.275";
56220
56324
  CODEX_VERSION = CODEX_APP_SERVER_SUPPORTED_VERSION;
56221
56325
  RUNTIMES_MODE_ENV = "FRIZZ_RUNTIMES";
56222
56326
  RUNTIMES_DIR_ENV = "FRIZZ_RUNTIMES_DIR";
@@ -56313,7 +56417,7 @@ var init_runtimes = __esm({
56313
56417
  case "7": {
56314
56418
  const file = safeEntryPath(this.dest, name);
56315
56419
  if (!file) return;
56316
- mkdirSync21(dirname22(file), { recursive: true });
56420
+ mkdirSync21(dirname23(file), { recursive: true });
56317
56421
  this.fd = openSync17(file, "w", octal(header, 100, 8) & 511 || 420);
56318
56422
  this.written.push(file);
56319
56423
  return;
@@ -56372,11 +56476,15 @@ function createTenantMap(options) {
56372
56476
  };
56373
56477
  async function activate(project) {
56374
56478
  const already = open.get(project.id);
56375
- if (already) return already.ctx;
56479
+ if (already && already.project.dir === project.dir) return already.ctx;
56376
56480
  const inFlight = opening.get(project.id);
56377
56481
  if (inFlight) return inFlight;
56378
56482
  const attempt = (async () => {
56379
56483
  try {
56484
+ if (already) {
56485
+ log.info("tenants", `project ${project.name} (${project.id}) moved from ${already.project.dir} to ${project.dir}; reopening it there`);
56486
+ await deactivate(project.id);
56487
+ }
56380
56488
  const ctx = await options.createContext({ ...options.contextOptions, project });
56381
56489
  open.set(project.id, { project, ctx, app: options.createApp?.(ctx) });
56382
56490
  await options.startProducers?.(ctx);
@@ -56891,12 +56999,12 @@ var init_legacy_project_db = __esm({
56891
56999
 
56892
57000
  // packages/server/src/frizz-db.ts
56893
57001
  import { existsSync as existsSync18, mkdirSync as mkdirSync22, readdirSync as readdirSync14, renameSync as renameSync12, statSync as statSync25 } from "node:fs";
56894
- import { basename as basename13, dirname as dirname23, join as join46 } from "node:path";
57002
+ import { basename as basename13, dirname as dirname24, join as join46 } from "node:path";
56895
57003
  function frizzDatabaseLocation(options) {
56896
57004
  if (options.stateDir !== void 0) {
56897
- const projectsDir = dirname23(options.stateDir);
57005
+ const projectsDir = dirname24(options.stateDir);
56898
57006
  if (basename13(projectsDir) === "projects") {
56899
- return { path: options.path ?? join46(dirname23(projectsDir), "ui.db"), projectsDir };
57007
+ return { path: options.path ?? join46(dirname24(projectsDir), "ui.db"), projectsDir };
56900
57008
  }
56901
57009
  return { path: options.path ?? join46(options.stateDir, "ui.db"), projectsDir: void 0 };
56902
57010
  }
@@ -56920,7 +57028,7 @@ function ensureFrizzSchema(db) {
56920
57028
  }
56921
57029
  function openFrizzDatabase(options = {}) {
56922
57030
  const { path: path3, projectsDir } = frizzDatabaseLocation(options);
56923
- mkdirSync22(dirname23(path3), { recursive: true });
57031
+ mkdirSync22(dirname24(path3), { recursive: true });
56924
57032
  const db = new sqlite_default(path3);
56925
57033
  db.pragma("busy_timeout = 5000");
56926
57034
  db.pragma("journal_mode = WAL");
@@ -57144,7 +57252,7 @@ var init_tenant_prime = __esm({
57144
57252
  // packages/server/src/project-teardown.ts
57145
57253
  import { rmSync as rmSync13 } from "node:fs";
57146
57254
  import { homedir as homedir19 } from "node:os";
57147
- import { dirname as dirname24, resolve as resolve10 } from "node:path";
57255
+ import { dirname as dirname25, resolve as resolve10 } from "node:path";
57148
57256
  async function stopProjectWorkers(ctx) {
57149
57257
  let stopped = 0;
57150
57258
  for (const row of ctx.storage.allSessions()) {
@@ -57164,7 +57272,7 @@ function deleteProjectState(projectId, home = homedir19()) {
57164
57272
  if (!/^[\w.-]+$/u.test(projectId) || projectId === "." || projectId === "..") {
57165
57273
  throw new Error(`Refusing to delete state for a malformed project id: ${projectId}`);
57166
57274
  }
57167
- if (dirname24(resolve10(target)) !== resolve10(dirname24(projectStateDir("x", home)))) {
57275
+ if (dirname25(resolve10(target)) !== resolve10(dirname25(projectStateDir("x", home)))) {
57168
57276
  throw new Error(`Refusing to delete ${target} \u2014 it is not a project state directory`);
57169
57277
  }
57170
57278
  rmSync13(target, { recursive: true, force: true });
@@ -57194,7 +57302,7 @@ __export(index_exports, {
57194
57302
  });
57195
57303
  import { createServer } from "node:http";
57196
57304
  import { readFileSync as readFileSync33, existsSync as existsSync19, statSync as statSync26 } from "node:fs";
57197
- import { dirname as dirname25, join as join47, resolve as resolve11, extname as extname4, posix as posix2, sep as sep6 } from "node:path";
57305
+ import { dirname as dirname26, join as join47, resolve as resolve11, extname as extname4, posix as posix2, sep as sep6 } from "node:path";
57198
57306
  function readServerAddressHolder(path3) {
57199
57307
  try {
57200
57308
  const value = JSON.parse(readFileSync33(path3, "utf8"));
@@ -57605,7 +57713,7 @@ async function startServer(opts = {}) {
57605
57713
  if (statusPath && statusIdentity) runtime.removeStatus(statusPath, statusIdentity);
57606
57714
  if (statusPath && statusIdentity) {
57607
57715
  try {
57608
- runtime.removeStatus(serverAddressPathForStateDir(dirname25(statusPath)), statusIdentity);
57716
+ runtime.removeStatus(serverAddressPathForStateDir(dirname26(statusPath)), statusIdentity);
57609
57717
  } catch {
57610
57718
  }
57611
57719
  }
@@ -57768,7 +57876,9 @@ async function startServer(opts = {}) {
57768
57876
  const entry = findProjectBySegment(split.slug);
57769
57877
  if (!entry) return void 0;
57770
57878
  const existing = tenants.appFor(entry.id);
57771
- if (existing) return { surfaces: existing, url: split.rest };
57879
+ if (existing && (entry.id === project.id || tenants.get(entry.id)?.project.dir === entry.path)) {
57880
+ return { surfaces: existing, url: split.rest };
57881
+ }
57772
57882
  if (!await tenants.activate(projectFromRegistryEntry(entry))) return void 0;
57773
57883
  const built = tenants.appFor(entry.id);
57774
57884
  return built ? { surfaces: built, url: split.rest } : void 0;