@agentdocumentformat/cli 0.7.8 → 0.8.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 (100) hide show
  1. package/README.md +1 -1
  2. package/dist/adf.mjs +59 -8
  3. package/dist/chunks/{APEv2Parser-2CNNHMXV.mjs → APEv2Parser-WIXRJLHB.mjs} +2 -2
  4. package/dist/chunks/{AiffParser-7BR4RL6G.mjs → AiffParser-ZC4QFOVJ.mjs} +5 -5
  5. package/dist/chunks/{App-7RIIOHJJ.mjs → App-A5GUFCFJ.mjs} +279 -293
  6. package/dist/chunks/{DsdiffParser-CMD4AX7A.mjs → DsdiffParser-UAPAJHFX.mjs} +5 -5
  7. package/dist/chunks/{DsfParser-UOGZDS2M.mjs → DsfParser-NNYZK2QX.mjs} +6 -6
  8. package/dist/chunks/{FlacParser-HB3T3CD4.mjs → FlacParser-MW2W4LO3.mjs} +6 -6
  9. package/dist/chunks/{MP4Parser-66HBRUSX.mjs → MP4Parser-Z57SYOOQ.mjs} +4 -4
  10. package/dist/chunks/{MpegParser-YW6LIUEW.mjs → MpegParser-XIIPAZVM.mjs} +7 -7
  11. package/dist/chunks/{MusepackParser-PAUSXP7D.mjs → MusepackParser-QWNDEYHJ.mjs} +6 -6
  12. package/dist/chunks/{OggParser-NUBV3GWC.mjs → OggParser-WRX53F77.mjs} +9 -9
  13. package/dist/chunks/{WavPackParser-WQAGT7TC.mjs → WavPackParser-MJ7WCSJ5.mjs} +2 -2
  14. package/dist/chunks/{WaveParser-EWLQVDX4.mjs → WaveParser-RAVLHZ5S.mjs} +7 -7
  15. package/dist/chunks/{adf-schema-US4NFHMR.mjs → adf-schema-YJSGLMA4.mjs} +2 -2
  16. package/dist/chunks/auth-manager-AJ32FZWS.mjs +19 -0
  17. package/dist/chunks/auth-manager-C5OBEQK2.mjs +18 -0
  18. package/dist/chunks/auth-manager-TBNKCJ45.mjs +17 -0
  19. package/dist/chunks/{chatgpt-subscription-E4MXTIAT.mjs → chatgpt-subscription-MMGNW5KJ.mjs} +5 -2
  20. package/dist/chunks/chunk-3UGZFWIF.mjs +26 -0
  21. package/dist/chunks/{chunk-5PO5PKRV.mjs → chunk-4OGZFUKA.mjs} +126 -67
  22. package/dist/chunks/{chunk-VN7UH7I5.mjs → chunk-6XQZGYMK.mjs} +246 -67
  23. package/dist/chunks/{chunk-XNPZBLYL.mjs → chunk-75DC5EWN.mjs} +13 -2
  24. package/dist/chunks/chunk-AZYOXXVL.mjs +865 -0
  25. package/dist/chunks/{chunk-6VQEXLE2.mjs → chunk-BHY7QDCM.mjs} +41 -3
  26. package/dist/chunks/chunk-C2S64Y6F.mjs +203 -0
  27. package/dist/chunks/chunk-C4GA5SI4.mjs +12799 -0
  28. package/dist/chunks/chunk-DDTTSKGG.mjs +2443 -0
  29. package/dist/chunks/{chunk-VAUOM3IN.mjs → chunk-DLWCKKIO.mjs} +1 -1
  30. package/dist/chunks/{chunk-QPLEAE6V.mjs → chunk-G6NBFYG5.mjs} +152 -824
  31. package/dist/chunks/{chunk-KXEF4F4E.mjs → chunk-GAZO6UMI.mjs} +1 -1
  32. package/dist/chunks/{chunk-XYTWGHFS.mjs → chunk-H3XLRWVE.mjs} +1 -1
  33. package/dist/chunks/{chunk-7T2YMTWL.mjs → chunk-H4C65MTP.mjs} +4 -4
  34. package/dist/chunks/chunk-HBOFZRUS.mjs +738 -0
  35. package/dist/chunks/chunk-JVPZIMXW.mjs +976 -0
  36. package/dist/chunks/{chunk-XYV3VCIX.mjs → chunk-JZFONJSL.mjs} +79 -20
  37. package/dist/chunks/chunk-KDDVQKCG.mjs +65 -0
  38. package/dist/chunks/chunk-KRPKWOMB.mjs +290 -0
  39. package/dist/chunks/{chunk-G6PH7RPW.mjs → chunk-LCMANRYY.mjs} +3 -3
  40. package/dist/chunks/{chunk-ZESQMP66.mjs → chunk-LGL6ML37.mjs} +3 -3
  41. package/dist/chunks/chunk-LKFHERRV.mjs +145 -0
  42. package/dist/chunks/chunk-LTX74NU6.mjs +29 -0
  43. package/dist/chunks/chunk-M2F7C2UJ.mjs +425 -0
  44. package/dist/chunks/{chunk-62NE4LG4.mjs → chunk-MQAMI5BF.mjs} +5 -5
  45. package/dist/chunks/chunk-MZRXFWPQ.mjs +278 -0
  46. package/dist/chunks/{chunk-PRMGZZM5.mjs → chunk-NZLEPJ5M.mjs} +2 -0
  47. package/dist/chunks/{chunk-CD3ATSXE.mjs → chunk-NZO5O4VN.mjs} +1 -1
  48. package/dist/chunks/{chunk-I3UYSITV.mjs → chunk-O7XKQ6CH.mjs} +1 -1
  49. package/dist/chunks/chunk-P3RG5LQA.mjs +244 -0
  50. package/dist/chunks/chunk-PAK5JCO2.mjs +48 -0
  51. package/dist/chunks/chunk-PC63YFB7.mjs +34 -0
  52. package/dist/chunks/{daemon-control-HKFVSQTQ.mjs → chunk-PRPTKMQW.mjs} +67 -26
  53. package/dist/chunks/{chunk-LFMB4PAA.mjs → chunk-TKXIH27X.mjs} +974 -113
  54. package/dist/chunks/{chunk-5W7KES6P.mjs → chunk-UZPDETEU.mjs} +1 -1
  55. package/dist/chunks/chunk-V2T6NZX2.mjs +517 -0
  56. package/dist/chunks/chunk-V3MM6PCH.mjs +366 -0
  57. package/dist/chunks/chunk-WJGPNHGX.mjs +67 -0
  58. package/dist/chunks/chunk-WVTPPOKK.mjs +290 -0
  59. package/dist/chunks/{chunk-J74STFKE.mjs → chunk-WX6QCUWI.mjs} +272 -51
  60. package/dist/chunks/{chunk-Y5ZLKFLE.mjs → chunk-XNNJQCZP.mjs} +196 -165
  61. package/dist/chunks/{chunk-HDBPBORT.mjs → chunk-XT2BJUFC.mjs} +15 -6
  62. package/dist/chunks/{chunk-HSBV6YFU.mjs → chunk-YRMKSHW3.mjs} +614 -92
  63. package/dist/chunks/{chunk-BNXUAOO4.mjs → chunk-ZGXUOAWZ.mjs} +3186 -959
  64. package/dist/chunks/chunk-ZPB3HYM7.mjs +45 -0
  65. package/dist/chunks/claude-subscription-33BMZB6I.mjs +29 -0
  66. package/dist/chunks/{cli-NMFIAR7Z.mjs → cli-I47IFDHJ.mjs} +797 -56
  67. package/dist/chunks/{commands-KPJWFRUE.mjs → commands-E4EQUHE5.mjs} +4 -3
  68. package/dist/chunks/{daemon-YDIK3AAM.mjs → daemon-FQ5BV3AP.mjs} +82058 -63958
  69. package/dist/chunks/daemon-control-L3CLG63P.mjs +59 -0
  70. package/dist/chunks/{daemon-token-WRSULP7G.mjs → daemon-token-L5QKTHU5.mjs} +3 -1
  71. package/dist/chunks/{discord-DSP4HUWI.mjs → discord-IR62565M.mjs} +3 -52
  72. package/dist/chunks/{email-BRG7KUO2.mjs → email-KHHTJBDW.mjs} +4 -2
  73. package/dist/chunks/{grok-subscription-4PYX7TBF.mjs → grok-subscription-E74FS5EG.mjs} +5 -2
  74. package/dist/chunks/{lib-N7FCZWKH.mjs → lib-L27L5PXM.mjs} +22 -22
  75. package/dist/chunks/{open-VAU7PXT6.mjs → open-TLGS2Z3Q.mjs} +4 -4
  76. package/dist/chunks/{parser-JVY2PWSM.mjs → parser-7GARNT5L.mjs} +1 -1
  77. package/dist/chunks/{pipeline-executor-O77NTGLP.mjs → pipeline-executor-FD4FBWQA.mjs} +6 -5
  78. package/dist/chunks/prompt-RRHZX4YT.mjs +13 -0
  79. package/dist/chunks/remote-access-cli-XXAPQWH6.mjs +268 -0
  80. package/dist/chunks/remote-hello-UFWUBDQI.mjs +37 -0
  81. package/dist/chunks/sdk-5FD2ZP7N.mjs +28422 -0
  82. package/dist/chunks/{slack-OCNNF4DX.mjs → slack-IYC5RAB5.mjs} +3 -4
  83. package/dist/chunks/{store-HUVDJPYN.mjs → store-ZRN2WCFB.mjs} +6 -3
  84. package/dist/chunks/tailscale-WLJATFMB.mjs +25 -0
  85. package/dist/chunks/{theme-SFE3JMA2.mjs → theme-HLBDTF6F.mjs} +1 -1
  86. package/dist/chunks/{themes-YLXLOIJF.mjs → themes-BQ72P446.mjs} +2 -2
  87. package/dist/chunks/{token-6G3HCMNX.mjs → token-6D22WECM.mjs} +20 -16
  88. package/dist/chunks/{token-util-UXYSDQIW.mjs → token-util-YF3ZWBZ4.mjs} +1 -2
  89. package/dist/chunks/{tui-UQJLK7XI.mjs → tui-SPSPDADM.mjs} +17 -14
  90. package/dist/chunks/{welcome-XHXFV7SH.mjs → welcome-J5JZZAM5.mjs} +1 -1
  91. package/dist/chunks/{whatsapp-AJMRQBT7.mjs → whatsapp-YJFFVN4Q.mjs} +9 -9
  92. package/package.json +1 -1
  93. package/dist/chunks/chunk-GKKCMXIZ.mjs +0 -94
  94. package/dist/chunks/chunk-IHDDCZBS.mjs +0 -112
  95. package/dist/chunks/chunk-KWFOS5WC.mjs +0 -41
  96. package/dist/chunks/chunk-UEZR2CNS.mjs +0 -65
  97. package/dist/chunks/chunk-VMHTYDH4.mjs +0 -172
  98. package/dist/chunks/chunk-Y2QA53AN.mjs +0 -259
  99. package/dist/chunks/{chunk-NBTUWMGP.mjs → chunk-2MTE22CR.mjs} +3 -3
  100. package/dist/chunks/{telegram-2EFGKIVK.mjs → telegram-JYY2VDTI.mjs} +5 -5
@@ -5,9 +5,11 @@ const require = __adfCreateRequire(import.meta.url);
5
5
  const __filename = __adfFileURLToPath(import.meta.url);
6
6
  const __dirname = __adfDirname(__filename);
7
7
  import {
8
- auth_flow_exports,
9
- openBrowser
10
- } from "./chunk-6VQEXLE2.mjs";
8
+ DaemonClient
9
+ } from "./chunk-6XQZGYMK.mjs";
10
+ import {
11
+ auth_flow_exports
12
+ } from "./chunk-BHY7QDCM.mjs";
11
13
  import {
12
14
  createMouseController,
13
15
  getScreenText,
@@ -20,6 +22,8 @@ import {
20
22
  setSidebarHidden
21
23
  } from "./chunk-APRU6PHC.mjs";
22
24
  import {
25
+ DaemonError,
26
+ MAIN_LOOP,
23
27
  PROMPT_PREFILL_KEY,
24
28
  SERVER_STOPPED_TEXT,
25
29
  StoreProvider,
@@ -29,6 +33,7 @@ import {
29
33
  folderName,
30
34
  isAsyncInput,
31
35
  isBusyState,
36
+ isCredentialsLocked,
32
37
  isTrackedKey,
33
38
  parseServer,
34
39
  servedText,
@@ -45,7 +50,8 @@ import {
45
50
  useClient,
46
51
  useStore,
47
52
  useTuiSelector
48
- } from "./chunk-J74STFKE.mjs";
53
+ } from "./chunk-WX6QCUWI.mjs";
54
+ import "./chunk-75DC5EWN.mjs";
49
55
  import {
50
56
  CHANNELS_OVERLAY,
51
57
  CHANNEL_ORDER,
@@ -117,10 +123,13 @@ import {
117
123
  validateCredentials,
118
124
  validateMcpForm,
119
125
  validateProvider
120
- } from "./chunk-QPLEAE6V.mjs";
126
+ } from "./chunk-AZYOXXVL.mjs";
121
127
  import {
122
128
  require_jsx_runtime
123
129
  } from "./chunk-7CV2SA3H.mjs";
130
+ import {
131
+ cjs
132
+ } from "./chunk-SGUTYISF.mjs";
124
133
  import {
125
134
  WELCOME_INTRO,
126
135
  WELCOME_OVERLAY,
@@ -130,36 +139,25 @@ import {
130
139
  nextStepIndex,
131
140
  welcomeFacts,
132
141
  welcomeSteps
133
- } from "./chunk-I3UYSITV.mjs";
142
+ } from "./chunk-O7XKQ6CH.mjs";
143
+ import {
144
+ getPrefs,
145
+ savePrefs
146
+ } from "./chunk-5QU5VY7N.mjs";
134
147
  import {
135
148
  THEMES,
136
149
  applyTheme,
137
150
  findTheme,
138
151
  nextThemeName,
139
152
  themeNames
140
- } from "./chunk-KXEF4F4E.mjs";
141
- import {
142
- DaemonClient
143
- } from "./chunk-VN7UH7I5.mjs";
153
+ } from "./chunk-GAZO6UMI.mjs";
144
154
  import {
145
- DaemonError,
146
- MAIN_LOOP,
147
- isCredentialsLocked
148
- } from "./chunk-XNPZBLYL.mjs";
149
- import {
150
- cjs
151
- } from "./chunk-SGUTYISF.mjs";
152
- import {
153
- getPrefs,
154
- savePrefs
155
- } from "./chunk-5QU5VY7N.mjs";
156
- import {
157
- checkFetchTarget,
158
- context_breakdown_exports
159
- } from "./chunk-Y2QA53AN.mjs";
160
- import "./chunk-2NAP2ZF7.mjs";
161
- import "./chunk-5PO5PKRV.mjs";
162
- import "./chunk-VMHTYDH4.mjs";
155
+ context_breakdown_exports,
156
+ guarded_fetch_exports,
157
+ skills_catalog_schema_exports
158
+ } from "./chunk-HBOFZRUS.mjs";
159
+ import "./chunk-4OGZFUKA.mjs";
160
+ import "./chunk-G6NBFYG5.mjs";
163
161
  import {
164
162
  Box_default,
165
163
  Text,
@@ -176,7 +174,7 @@ import {
176
174
  ThemeContext,
177
175
  stateColor,
178
176
  useTheme
179
- } from "./chunk-PRMGZZM5.mjs";
177
+ } from "./chunk-NZLEPJ5M.mjs";
180
178
  import {
181
179
  isMouseGarbage,
182
180
  lastRawInput,
@@ -192,21 +190,24 @@ import {
192
190
  import {
193
191
  require_react
194
192
  } from "./chunk-6GJ4QHRI.mjs";
193
+ import {
194
+ ADF_SKILLS_REGISTRY_URL
195
+ } from "./chunk-LCMANRYY.mjs";
195
196
  import "./chunk-UYY5M2PB.mjs";
196
- import "./chunk-GKKCMXIZ.mjs";
197
- import "./chunk-UEZR2CNS.mjs";
198
- import "./chunk-IHDDCZBS.mjs";
197
+ import "./chunk-2NAP2ZF7.mjs";
198
+ import "./chunk-LKFHERRV.mjs";
199
+ import "./chunk-V2T6NZX2.mjs";
200
+ import "./chunk-WVTPPOKK.mjs";
201
+ import "./chunk-WJGPNHGX.mjs";
202
+ import "./chunk-PAK5JCO2.mjs";
203
+ import "./chunk-ZPB3HYM7.mjs";
204
+ import "./chunk-LTX74NU6.mjs";
199
205
  import {
200
206
  require_dist
201
207
  } from "./chunk-APHX6HSV.mjs";
202
- import {
203
- external_exports
204
- } from "./chunk-ZJKXHM4Q.mjs";
208
+ import "./chunk-NZO5O4VN.mjs";
209
+ import "./chunk-ZJKXHM4Q.mjs";
205
210
  import "./chunk-RIX34QHC.mjs";
206
- import {
207
- ADF_SKILLS_REGISTRY_URL
208
- } from "./chunk-G6PH7RPW.mjs";
209
- import "./chunk-CD3ATSXE.mjs";
210
211
  import {
211
212
  __export,
212
213
  __toESM
@@ -510,10 +511,10 @@ function TextInput(props) {
510
511
  var import_react3 = __toESM(require_react(), 1);
511
512
 
512
513
  // src/main/tui/auth/model.ts
513
- var AUTH_OVERLAY = "auth", SUBSCRIPTIONS = ["chatgpt", "grok"], SUBSCRIPTION_LABELS = { chatgpt: "ChatGPT", grok: "Grok" }, AUTH_EXPLAINER = "The daemon keeps its own sign-in, separate from ADF Studio\u2019s. Agents that use these providers need no changes.";
514
+ var AUTH_OVERLAY = "auth", SUBSCRIPTIONS = ["chatgpt", "grok", "claude"], SUBSCRIPTION_LABELS = { chatgpt: "ChatGPT", grok: "Grok", claude: "Claude" }, CLAUDE_EXPLAINER = "Claude signs in through Claude Code on the daemon host; ADF stores no Claude credentials. The sign-in is shared with Claude Code there.", AUTH_EXPLAINER = "The daemon keeps its own sign-in, separate from ADF Studio\u2019s. Agents that use these providers need no changes.";
514
515
  function subscriptionOfType(type, id) {
515
516
  let t = `${type ?? ""} ${id ?? ""}`.toLowerCase();
516
- return t.includes("chatgpt-subscription") || !type && /chatgpt/.test(t) ? "chatgpt" : t.includes("grok-subscription") || !type && /grok/.test(t) ? "grok" : null;
517
+ return t.includes("chatgpt-subscription") || !type && /chatgpt/.test(t) ? "chatgpt" : t.includes("grok-subscription") || !type && /grok/.test(t) ? "grok" : t.includes("claude-subscription") ? "claude" : null;
517
518
  }
518
519
  function statusOf(auth, provider) {
519
520
  let value = auth?.[provider];
@@ -540,9 +541,14 @@ function formatExpiry(expiresAt, now = Date.now()) {
540
541
  }
541
542
  function describeStatus(status) {
542
543
  if (!status) return { text: "unknown", ok: !1 };
544
+ if (status.installed === !1) return { text: `Claude Code not installed${status.installUrl ? ` (${status.installUrl})` : ""}`, ok: !1 };
545
+ if (status.updateRequired) return { text: `Claude Code ${status.version ?? ""} is too old \xB7 ADF needs ${status.minVersion ?? "a newer version"} or newer (run \`claude update\`)`, ok: !1 };
546
+ let cc = status.version ? ` \xB7 Claude Code ${status.version}` : "";
543
547
  if (status.authenticated) {
548
+ let failed = status.flowError && !status.flowPending ? ` \xB7 last sign-in failed: ${status.flowError}` : "";
549
+ if (status.plan) return { text: `signed in${status.email ? ` as ${status.email}` : ""} \xB7 ${status.plan} plan${cc}${failed}`, ok: !0 };
544
550
  let expiry = formatExpiry(status.expiresAt);
545
- return { text: `signed in${status.email ? ` as ${status.email}` : ""}${expiry ? ` \xB7 token ${expiry.startsWith("expired") ? expiry : `renews in ${expiry}`}` : ""}`, ok: !0 };
551
+ return { text: `signed in${status.email ? ` as ${status.email}` : ""}${expiry ? ` \xB7 token ${expiry.startsWith("expired") ? expiry : `renews in ${expiry}`}` : ""}${failed}`, ok: !0 };
546
552
  }
547
553
  return status.flowPending ? { text: "sign-in in progress", ok: !1 } : status.flowError ? { text: `not signed in (last attempt: ${status.flowError})`, ok: !1 } : { text: "not signed in", ok: !1 };
548
554
  }
@@ -1039,6 +1045,9 @@ function completePath(input, options = {}) {
1039
1045
  }
1040
1046
  return { value: dirPart + (common.length > base2.length ? common : base2), candidates };
1041
1047
  }
1048
+ function lockedNote(fields) {
1049
+ return fields && fields.length > 0 ? `; locked against agent edits: ${fields.join(", ")}` : "";
1050
+ }
1042
1051
 
1043
1052
  // src/main/tui/views/fleet/data.ts
1044
1053
  var import_react4 = __toESM(require_react(), 1);
@@ -1252,13 +1261,14 @@ ${bits}` : "";
1252
1261
  if (!await store.actions.confirm({
1253
1262
  title: "Review required",
1254
1263
  message: `${filePath} has not been reviewed on this daemon.${summary}
1255
- Accept the review and load it?`,
1264
+ Accept the review and load it? Accepting also locks its security-sensitive settings against the agent's own edits, as Studio does.`,
1256
1265
  confirmLabel: "Accept + load",
1257
1266
  danger: !0
1258
1267
  }))
1259
1268
  return store.actions.toast("Not loaded (review declined)", "warn"), !1;
1260
1269
  try {
1261
- return await store.client.acceptReview(filePath), store.actions.toast(`Review accepted for ${filePath}`, "success"), !0;
1270
+ let accepted = await store.client.acceptReview(filePath);
1271
+ return store.actions.toast(`Review accepted for ${filePath}${lockedNote(accepted.lockedFields)}`, "success", 6e3), !0;
1262
1272
  } catch (err) {
1263
1273
  return failure(store, null, "Accept review", err), !1;
1264
1274
  }
@@ -3832,7 +3842,7 @@ function wakesText(timers, now = Date.now(), sep4 = "\xB7") {
3832
3842
  }
3833
3843
 
3834
3844
  // src/main/tui/auth/flow.ts
3835
- var { loginChatGpt, loginGrok, openBrowser: openBrowser2 } = cjs(auth_flow_exports), seams = { openBrowser: openBrowser2 };
3845
+ var { loginChatGpt, loginClaude, loginGrok, openBrowser } = cjs(auth_flow_exports), seams = { openBrowser };
3836
3846
  function openUrl(url2) {
3837
3847
  seams.openBrowser(url2);
3838
3848
  }
@@ -3845,7 +3855,7 @@ async function signIn(client, provider, signal, handlers) {
3845
3855
  startCallbackServer: seams.startCallbackServer
3846
3856
  };
3847
3857
  try {
3848
- return provider === "chatgpt" ? await loginChatGpt(deps, { daemonUrl: client.baseUrl, onStart: (info) => handlers.onChatGpt?.(info) }) : await loginGrok(deps, { onStart: (info) => handlers.onGrok?.(info) });
3858
+ return provider === "claude" ? await loginClaude(deps, { onStart: (info) => handlers.onClaude?.(info) }) : provider === "chatgpt" ? await loginChatGpt(deps, { daemonUrl: client.baseUrl, onStart: (info) => handlers.onChatGpt?.(info) }) : await loginGrok(deps, { onStart: (info) => handlers.onGrok?.(info) });
3849
3859
  } catch (err) {
3850
3860
  return signal.aborted ? { ok: !1, error: "Sign-in cancelled", cancelled: !0 } : { ok: !1, error: err instanceof Error ? err.message : String(err) };
3851
3861
  }
@@ -3854,14 +3864,15 @@ async function signIn(client, provider, signal, handlers) {
3854
3864
  // src/main/tui/auth/AuthDialog.tsx
3855
3865
  var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
3856
3866
  function AuthDialog({ overlay, close, width }) {
3857
- let theme2 = useTheme(), store = useStore(), auth = useTuiSelector((s) => s.auth), props = overlay.props ?? {}, [cursor, setCursor] = (0, import_react15.useState)(Math.max(0, SUBSCRIPTIONS.indexOf(props.login ?? "chatgpt"))), [flow, setFlow] = (0, import_react15.useState)({ kind: "list" }), [note, setNote] = (0, import_react15.useState)(null), abort = (0, import_react15.useRef)(null), dialogWidth = Math.max(50, Math.min(width - 4, 84));
3867
+ let theme2 = useTheme(), store = useStore(), auth = useTuiSelector((s) => s.auth), props = overlay.props ?? {}, [cursor, setCursor] = (0, import_react15.useState)(Math.max(0, SUBSCRIPTIONS.indexOf(props.login ?? "chatgpt"))), [flow, setFlow] = (0, import_react15.useState)({ kind: "list" }), [note, setNote] = (0, import_react15.useState)(null), [pasted, setPasted] = (0, import_react15.useState)(""), abort = (0, import_react15.useRef)(null), dialogWidth = Math.max(50, Math.min(width - 4, 84));
3858
3868
  (0, import_react15.useEffect)(() => (store.actions.refreshAuth(), props.login && start(props.login), () => abort.current?.abort()), []);
3859
3869
  let start = (provider) => {
3860
3870
  abort.current?.abort();
3861
3871
  let ctrl = new AbortController();
3862
- abort.current = ctrl, setNote(null), setFlow({ kind: "running", provider }), signIn(store.client, provider, ctrl.signal, {
3872
+ abort.current = ctrl, setNote(null), setPasted(""), setFlow({ kind: "running", provider }), signIn(store.client, provider, ctrl.signal, {
3863
3873
  onChatGpt: (info) => setFlow({ kind: "running", provider, url: info.authUrl, relay: info.mode === "relay" ? info.redirectUri : void 0 }),
3864
- onGrok: (info) => setFlow({ kind: "running", provider, url: info.verificationUri, code: info.userCode })
3874
+ onGrok: (info) => setFlow({ kind: "running", provider, url: info.verificationUri, code: info.userCode }),
3875
+ onClaude: (info) => setFlow({ kind: "running", provider, url: info.url || void 0, started: !0 })
3865
3876
  }).then((outcome) => {
3866
3877
  if (ctrl.signal.aborted && !outcome.ok) return;
3867
3878
  abort.current = null, store.actions.refreshAuth();
@@ -3870,6 +3881,12 @@ function AuthDialog({ overlay, close, width }) {
3870
3881
  });
3871
3882
  }, cancel = () => {
3872
3883
  flow.kind === "running" && (abort.current?.abort(), abort.current = null, setNote(`${SUBSCRIPTION_LABELS[flow.provider]} sign-in cancelled.`), setFlow({ kind: "list" }));
3884
+ }, sendCode = (value) => {
3885
+ let code = value.trim();
3886
+ return code ? (setPasted(""), setNote("Sending the code to Claude Code\u2026"), store.client.submitClaudeCode(code).then(
3887
+ () => setNote("Code sent. Waiting for Claude Code to finish\u2026"),
3888
+ (err) => setNote(`Could not send the code: ${err instanceof Error ? err.message : String(err)}`)
3889
+ ), !0) : !1;
3873
3890
  }, copyUrl = (url2) => {
3874
3891
  url2 && copyToClipboard(url2).then((ok) => setNote(ok ? "URL copied to the clipboard." : "Could not copy; select the URL with the mouse instead."));
3875
3892
  };
@@ -3894,25 +3911,30 @@ function AuthDialog({ overlay, close, width }) {
3894
3911
  } else (input === "n" || key.escape) && setFlow({ kind: "list" });
3895
3912
  return !0;
3896
3913
  case "running":
3897
- return key.escape ? (cancel(), !0) : (input === "c" && copyUrl(flow.url), !0);
3914
+ return key.escape ? (cancel(), !0) : (input === "c" && flow.provider !== "claude" && copyUrl(flow.url), !0);
3898
3915
  case "done":
3899
3916
  return key.escape || key.return ? (flow.outcome.ok ? close() : setFlow({ kind: "list" }), !0) : input === "r" && !flow.outcome.ok ? (start(flow.provider), !0) : (input === "c" && copyUrl(flow.url), !0);
3900
3917
  }
3901
3918
  }, { layer: "overlay" });
3902
3919
  let noteLine = note ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.muted, wrap: "wrap", children: note }) : null;
3903
3920
  if (flow.kind === "running" || flow.kind === "done") {
3904
- let label2 = SUBSCRIPTION_LABELS[flow.provider], running2 = flow.kind === "running", code = running2 ? flow.code : void 0, url2 = flow.url, hints2 = running2 ? [...url2 ? [{ keys: "c", label: "copy URL" }] : [], { keys: "esc", label: "cancel" }] : flow.outcome.ok ? [{ keys: "enter", label: "close" }] : [{ keys: "r", label: "try again" }, { keys: "esc", label: "back" }];
3921
+ let label2 = SUBSCRIPTION_LABELS[flow.provider], running2 = flow.kind === "running", code = running2 ? flow.code : void 0, url2 = flow.url, claude = flow.provider === "claude", claudeWaiting = running2 && claude && !!flow.started, hints2 = running2 ? claude ? [...claudeWaiting ? [{ keys: "enter", label: "send code" }] : [], { keys: "esc", label: "cancel" }] : [...url2 ? [{ keys: "c", label: "copy URL" }] : [], { keys: "esc", label: "cancel" }] : flow.outcome.ok ? [{ keys: "enter", label: "close" }] : [{ keys: "r", label: "try again" }, { keys: "esc", label: "back" }];
3905
3922
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Modal, { title: `Sign in to ${label2}`, width: dialogWidth, hints: hints2, children: [
3906
- running2 && !url2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner, { label: `asking the daemon to start ${label2} sign-in\u2026` }) : null,
3923
+ running2 && !url2 && !(running2 && flow.started) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner, { label: `asking the daemon to start ${label2} sign-in\u2026` }) : null,
3924
+ claude ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.muted, wrap: "wrap", children: CLAUDE_EXPLAINER }) : null,
3907
3925
  code ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Box_default, { flexDirection: "column", alignItems: "center", marginBottom: 1, children: [
3908
3926
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.muted, children: "Enter this code in the browser:" }),
3909
3927
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { borderStyle: theme2.ascii ? "classic" : "double", borderColor: theme2.color.accent, paddingX: 3, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { bold: !0, color: theme2.color.accent, children: spacedCode(code) }) })
3910
3928
  ] }) : null,
3911
3929
  url2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3912
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.muted, children: code ? "on this page (the browser should open it):" : "The browser should open. If it does not, open this URL:" }),
3930
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.muted, children: code ? "on this page (the browser should open it):" : claude ? "Claude Code is opening the sign-in page in the browser on the daemon host. If it does not open there, open this URL:" : "The browser should open. If it does not, open this URL:" }),
3913
3931
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.info, wrap: "wrap", children: url2 })
3914
3932
  ] }) : null,
3915
- running2 && url2 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner, { label: flow.relay ? `waiting for you to finish in the browser\u2026 (callback on ${flow.relay})` : "waiting for you to finish in the browser\u2026" }) }) : null,
3933
+ claudeWaiting ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
3934
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { color: theme2.color.text, children: "If the page shows a code, paste it here (or wait):" }),
3935
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { borderStyle: theme2.ascii ? "classic" : "round", borderColor: theme2.color.borderFocus, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TextInput, { value: pasted, onChange: setPasted, onSubmit: sendCode, focused: !0, keyLayer: "overlay", maxRows: 2, placeholder: "code from the Claude sign-in page" }) })
3936
+ ] }) : null,
3937
+ running2 && (url2 || claudeWaiting) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner, { label: flow.relay ? `waiting for you to finish in the browser\u2026 (callback on ${flow.relay})` : "waiting for you to finish in the browser\u2026" }) }) : null,
3916
3938
  running2 ? null : flow.outcome.ok ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { bold: !0, color: theme2.color.success, children: [
3917
3939
  theme2.glyph.check,
3918
3940
  " Signed in to ",
@@ -4484,8 +4506,8 @@ var import_jsx_runtime24 = __toESM(require_jsx_runtime(), 1), MODEL_OVERLAY = "i
4484
4506
  function providerChoices(providers, auth) {
4485
4507
  return providers.map((p) => {
4486
4508
  let type = String(p.type ?? ""), id = String(p.id ?? ""), name = String(p.name ?? id);
4487
- if (type === "chatgpt-subscription" || type === "grok-subscription") {
4488
- let signedIn = type === "chatgpt-subscription" ? auth?.chatgpt : auth?.grok;
4509
+ if (type === "chatgpt-subscription" || type === "grok-subscription" || type === "claude-subscription") {
4510
+ let signedIn = type === "chatgpt-subscription" ? auth?.chatgpt : type === "grok-subscription" ? auth?.grok : auth?.claude;
4489
4511
  return { id, name, type, access: signedIn?.authenticated ? "signed in" : "not signed in (/login)", ready: !!signedIn?.authenticated };
4490
4512
  }
4491
4513
  if (p.hasApiKey === !0) return { id, name, type, access: "key set", ready: !0 };
@@ -4633,14 +4655,14 @@ var { normalizeAuthProvider } = cjs(auth_flow_exports);
4633
4655
  function openAuth(actions4, props = {}) {
4634
4656
  actions4.pushOverlay({ kind: AUTH_OVERLAY, props: { ...props } });
4635
4657
  }
4636
- var complete = (partial) => ["chatgpt", "grok"].filter((v) => v.startsWith(partial.trim().toLowerCase())), authCommand = {
4658
+ var complete = (partial) => ["chatgpt", "grok", "claude"].filter((v) => v.startsWith(partial.trim().toLowerCase())), authCommand = {
4637
4659
  name: "auth",
4638
- description: "Provider sign-in: ChatGPT and Grok status, sign in, sign out",
4660
+ description: "Provider sign-in: ChatGPT, Grok and Claude status, sign in, sign out",
4639
4661
  run: (ctx) => openAuth(ctx.actions)
4640
4662
  }, loginCommand = {
4641
4663
  name: "login",
4642
- args: "[chatgpt|grok]",
4643
- description: "Sign the daemon in to ChatGPT (browser) or Grok (device code)",
4664
+ args: "[chatgpt|grok|claude]",
4665
+ description: "Sign the daemon in to ChatGPT (browser), Grok (device code) or Claude (Claude Code on the daemon host)",
4644
4666
  complete,
4645
4667
  run: (ctx) => {
4646
4668
  if (!ctx.args[0]) {
@@ -4649,29 +4671,30 @@ var complete = (partial) => ["chatgpt", "grok"].filter((v) => v.startsWith(parti
4649
4671
  }
4650
4672
  let provider = normalizeAuthProvider(ctx.args[0]);
4651
4673
  if (!provider) {
4652
- ctx.print("Usage: /login [chatgpt|grok]", "warn");
4674
+ ctx.print("Usage: /login [chatgpt|grok|claude]", "warn");
4653
4675
  return;
4654
4676
  }
4655
4677
  openAuth(ctx.actions, { login: provider });
4656
4678
  }
4657
4679
  }, logoutCommand = {
4658
4680
  name: "logout",
4659
- args: "<chatgpt|grok>",
4660
- description: "Sign the daemon out of ChatGPT or Grok (asks)",
4681
+ args: "<chatgpt|grok|claude>",
4682
+ description: "Sign the daemon out of ChatGPT, Grok or Claude (asks)",
4661
4683
  complete,
4662
4684
  run: async (ctx) => {
4663
4685
  let provider = normalizeAuthProvider(ctx.args[0]);
4664
4686
  if (!provider) {
4665
- ctx.print("Usage: /logout <chatgpt|grok>", "warn");
4687
+ ctx.print("Usage: /logout <chatgpt|grok|claude>", "warn");
4666
4688
  return;
4667
4689
  }
4668
- let label2 = SUBSCRIPTION_LABELS[provider];
4669
- await ctx.actions.confirm({ title: `Sign out of ${label2}`, message: `Sign the daemon out of ${label2}? Agents using it stop working until you sign in again.`, confirmLabel: "Sign out", danger: !0 }) && await ctx.actions.logoutSubscription(provider);
4690
+ let label2 = SUBSCRIPTION_LABELS[provider], shared = provider === "claude" ? " This also signs Claude Code out on the daemon host." : "";
4691
+ await ctx.actions.confirm({ title: `Sign out of ${label2}`, message: `Sign the daemon out of ${label2}? Agents using it stop working until you sign in again.${shared}`, confirmLabel: "Sign out", danger: !0 }) && await ctx.actions.logoutSubscription(provider);
4670
4692
  }
4671
4693
  }, authActions = [
4672
- { id: "shell.auth", title: "Provider sign-in", hint: "ChatGPT, Grok", group: "Actions", keywords: ["auth", "login", "sign in", "subscription", "account"], run: (ctx) => openAuth(ctx.actions) },
4694
+ { id: "shell.auth", title: "Provider sign-in", hint: "ChatGPT, Grok, Claude", group: "Actions", keywords: ["auth", "login", "sign in", "subscription", "account"], run: (ctx) => openAuth(ctx.actions) },
4673
4695
  { id: "shell.auth.chatgpt", title: "Sign in to ChatGPT", group: "Actions", keywords: ["login", "openai", "codex", "auth"], available: (s) => !statusOf(s.state().auth, "chatgpt")?.authenticated, run: (ctx) => openAuth(ctx.actions, { login: "chatgpt" }) },
4674
- { id: "shell.auth.grok", title: "Sign in to Grok", group: "Actions", keywords: ["login", "xai", "auth", "device code"], available: (s) => !statusOf(s.state().auth, "grok")?.authenticated, run: (ctx) => openAuth(ctx.actions, { login: "grok" }) }
4696
+ { id: "shell.auth.grok", title: "Sign in to Grok", group: "Actions", keywords: ["login", "xai", "auth", "device code"], available: (s) => !statusOf(s.state().auth, "grok")?.authenticated, run: (ctx) => openAuth(ctx.actions, { login: "grok" }) },
4697
+ { id: "shell.auth.claude", title: "Sign in to Claude", group: "Actions", keywords: ["login", "anthropic", "claude code", "auth", "subscription"], available: (s) => !statusOf(s.state().auth, "claude")?.authenticated, run: (ctx) => openAuth(ctx.actions, { login: "claude" }) }
4675
4698
  ];
4676
4699
 
4677
4700
  // src/main/tui/setup/ProviderDialog.tsx
@@ -5535,7 +5558,7 @@ async function editText(text, options = {}) {
5535
5558
  var schemaParse = null;
5536
5559
  async function loadSchema() {
5537
5560
  if (schemaParse) return schemaParse;
5538
- let schema = cjs(await import("./adf-schema-US4NFHMR.mjs")).AgentConfigSchema;
5561
+ let schema = cjs(await import("./adf-schema-YJSGLMA4.mjs")).AgentConfigSchema;
5539
5562
  return schemaParse = (value) => schema.safeParse(value), schemaParse;
5540
5563
  }
5541
5564
  function changedKeysBetween(a, b) {
@@ -5740,6 +5763,10 @@ function TemplatesDialog({ overlay, close: closeOverlay, width, height }) {
5740
5763
  }, openReview = async (id, back) => {
5741
5764
  let r = await act("Review", back, () => store.client.templateReview(id));
5742
5765
  if (r) {
5766
+ if (r.identityReady === !1) {
5767
+ close(), openIdentity(store, { mode: "status", then: TEMPLATES_OVERLAY, thenProps: { id }, reason: `Review: ${r.message ?? "Set up the owner identity first."}` });
5768
+ return;
5769
+ }
5743
5770
  if (!r.needsReview) {
5744
5771
  await refresh2(), done(`${label2(id)} is already reviewed`, back);
5745
5772
  return;
@@ -5883,7 +5910,7 @@ function TemplatesDialog({ overlay, close: closeOverlay, width, height }) {
5883
5910
  }
5884
5911
  if (step.kind === "review")
5885
5912
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Modal, { title: `Review ${theme2.glyph.sep} ${label2(step.id)} template`, width: dialogWidth, hints: [{ keys: "y enter", label: "claim + accept" }, { keys: "up down", label: "scroll" }, { keys: "n esc", label: "cancel" }], children: [
5886
- step.review ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LinesView, { lines: reviewLines(step.review.summary, inner), width: inner, height: fitLines(reviewLines(step.review.summary, inner).length), keyLayer: "overlay" }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Spinner, { label: "reading the template\u2026" }),
5913
+ step.review?.summary ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LinesView, { lines: reviewLines(step.review.summary, inner), width: inner, height: fitLines(reviewLines(step.review.summary, inner).length), keyLayer: "overlay" }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Spinner, { label: "reading the template\u2026" }),
5887
5914
  errorLine
5888
5915
  ] });
5889
5916
  if (step.kind === "files") {
@@ -6484,20 +6511,62 @@ function describeDiff(before, after, preview = 6) {
6484
6511
  return diff.changes.length > preview && lines.push(` \u2026 ${diff.changes.length - preview} more changed lines`), { summary, lines };
6485
6512
  }
6486
6513
 
6514
+ // src/shared/types/resource-revision.types.ts
6515
+ var resource_revision_types_exports = {};
6516
+ __export(resource_revision_types_exports, {
6517
+ describeWriter: () => describeWriter
6518
+ });
6519
+ function describeWriter(writer2) {
6520
+ if (!writer2) return "someone else";
6521
+ switch (writer2.kind) {
6522
+ case "agent":
6523
+ return "the agent";
6524
+ case "cli":
6525
+ return "the CLI or another API client";
6526
+ case "client":
6527
+ return writer2.client === "studio" ? "another Studio window" : writer2.client === "tui" ? "the terminal app" : `another client (${writer2.client})`;
6528
+ }
6529
+ }
6530
+
6531
+ // src/main/tui/util/revisions.ts
6532
+ var { describeWriter: describeWriter2 } = cjs(resource_revision_types_exports);
6533
+ function revisionOf(result) {
6534
+ let revision = result?.revision;
6535
+ return typeof revision == "number" ? revision : void 0;
6536
+ }
6537
+ function staleRevisionOf(err) {
6538
+ if (!(err instanceof DaemonError) || err.status !== 409) return null;
6539
+ let body = err.body;
6540
+ return body?.code !== "stale_revision" || typeof body.revision != "number" ? null : { revision: body.revision, writer: body.writer ?? null };
6541
+ }
6542
+ function changedBy(stale) {
6543
+ return `changed by ${describeWriter2(stale.writer)}`;
6544
+ }
6545
+ async function writeOver(write2) {
6546
+ try {
6547
+ return { ok: await write2() };
6548
+ } catch (err) {
6549
+ let stale = staleRevisionOf(err);
6550
+ if (stale) return { stale };
6551
+ throw err;
6552
+ }
6553
+ }
6554
+
6487
6555
  // src/main/tui/views/files/io.ts
6488
6556
  async function loadTarget(client, agentId, target2) {
6489
6557
  if (target2.kind === "document" || target2.kind === "mind") {
6490
- let text2 = (target2.kind === "document" ? await client.document(agentId) : await client.mind(agentId)).content ?? "";
6491
- return { target: target2, text: text2, binary: !1, mime: "text/markdown", size: Buffer.byteLength(text2, "utf-8") };
6558
+ let result = target2.kind === "document" ? await client.document(agentId) : await client.mind(agentId), text2 = result.content ?? "";
6559
+ return { target: target2, text: text2, binary: !1, mime: "text/markdown", size: Buffer.byteLength(text2, "utf-8"), revision: revisionOf(result) };
6492
6560
  }
6493
- let file = await client.file(agentId, target2.path), base2 = { target: target2, mime: file.mime_type, size: file.size, protection: file.protection, authorized: file.authorized, updatedAt: file.updated_at };
6561
+ let file = await client.file(agentId, target2.path), base2 = { target: target2, mime: file.mime_type, size: file.size, protection: file.protection, authorized: file.authorized, updatedAt: file.updated_at, revision: revisionOf(file) };
6494
6562
  if (file.encoding === "base64")
6495
6563
  return { ...base2, binary: !0, bytes: Buffer.from(file.content_base64 ?? "", "base64") };
6496
6564
  let text = file.content ?? "";
6497
6565
  return looksBinary(text) ? { ...base2, binary: !0, bytes: Buffer.from(text, "utf-8") } : { ...base2, text, binary: !1 };
6498
6566
  }
6499
- async function writeTarget(client, agentId, target2, text) {
6500
- target2.kind === "document" ? await client.putDocument(agentId, text) : target2.kind === "mind" ? await client.putMind(agentId, text) : await client.writeFile(agentId, target2.path, { content: text });
6567
+ async function writeTarget(client, agentId, target2, text, baseRevision) {
6568
+ let base2 = baseRevision === "absent" ? { mustNotExist: !0 } : baseRevision !== void 0 ? { baseRevision } : {}, agent2 = `/agents/${encodeURIComponent(agentId)}`;
6569
+ return target2.kind === "document" ? revisionOf(await client.request("PUT", `${agent2}/document`, { body: { content: text, ...base2 } })) : target2.kind === "mind" ? revisionOf(await client.request("PUT", `${agent2}/mind`, { body: { content: text, ...base2 } })) : revisionOf(await client.request("PUT", `${agent2}/files/content`, { query: { path: target2.path }, body: { content: text, ...base2 } }));
6501
6570
  }
6502
6571
  var pathCache = /* @__PURE__ */ new Map();
6503
6572
  function cacheFiles(agentId, files2) {
@@ -6549,13 +6618,13 @@ async function editTarget(deps, target2, options = {}) {
6549
6618
  }
6550
6619
  if (original.charCodeAt(0) !== 65279 && (edited = stripBom2(edited)), edited === original && !options.create)
6551
6620
  return await cleanup(), actions4.toast(`No changes to ${label2}`, "info"), "unchanged";
6552
- let concurrent = null;
6621
+ let concurrent = null, baseRevision = options.create ? "absent" : void 0;
6553
6622
  if (options.create)
6554
6623
  target2.kind === "file" && (await client.files(agentId).catch(() => null))?.files.some((f) => f.path === target2.path) && (concurrent = "was created by someone else while you were editing");
6555
6624
  else
6556
6625
  try {
6557
6626
  let current = await loadTarget(client, agentId, target2);
6558
- current.text !== original && (concurrent = `changed while you were editing (their change: ${describeDiff(original, current.text ?? "", 0).summary})`);
6627
+ baseRevision = current.revision, current.text !== original && (concurrent = `changed while you were editing (their change: ${describeDiff(original, current.text ?? "", 0).summary})`);
6559
6628
  } catch (err) {
6560
6629
  concurrent = err.status === 404 ? "was deleted while you were editing" : `could not be re-checked (${err instanceof Error ? err.message : String(err)})`;
6561
6630
  }
@@ -6565,195 +6634,44 @@ async function editTarget(deps, target2, options = {}) {
6565
6634
  ...diff.lines
6566
6635
  ].filter((line) => line !== null).join(`
6567
6636
  `);
6568
- return await actions4.confirm({
6637
+ if (!await actions4.confirm({
6569
6638
  title: concurrent ? `Concurrent change: ${label2}` : `${options.create ? "Create" : "Save"} ${label2}`,
6570
6639
  message,
6571
6640
  confirmLabel: concurrent ? "Overwrite" : options.create ? "Create" : "Write",
6572
6641
  cancelLabel: "Discard",
6573
6642
  danger: !!concurrent
6574
- }) ? await actions4.run(`Write ${label2}`, (c) => writeTarget(c, agentId, target2, edited).then(() => !0)) ? (await cleanup(), actions4.toast(`${options.create ? "Created" : "Saved"} ${label2} (${diff.summary})`, "success"), "written") : (actions4.toast(`Write failed. Your edited copy is kept at ${file}`, "error", 12e3), "failed") : (actions4.toast(`Not written. Your edited copy is kept at ${file}`, "warn", 12e3), "discarded");
6575
- }
6576
-
6577
- // src/main/utils/guarded-fetch.ts
6578
- var guarded_fetch_exports = {};
6579
- __export(guarded_fetch_exports, {
6580
- DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT,
6581
- MAX_FETCH_REDIRECTS: () => MAX_FETCH_REDIRECTS,
6582
- daemonGuardPort: () => daemonGuardPort,
6583
- guardedFetch: () => guardedFetch,
6584
- isFetchFailure: () => isFetchFailure
6585
- });
6586
- var DEFAULT_DAEMON_PORT = 7385, MAX_FETCH_REDIRECTS = 3;
6587
- function daemonGuardPort() {
6588
- return Number(process.env.ADF_DAEMON_PORT) || DEFAULT_DAEMON_PORT;
6589
- }
6590
- function isFetchFailure(result) {
6591
- return "error" in result;
6643
+ }))
6644
+ return actions4.toast(`Not written. Your edited copy is kept at ${file}`, "warn", 12e3), "discarded";
6645
+ let written = await writeOverConflicts(actions4, label2, file, (base2) => actions4.run(`Write ${label2}`, (c) => writeOver(() => writeTarget(c, agentId, target2, edited, base2))), baseRevision);
6646
+ return written === "reloaded" ? "discarded" : written ? (await cleanup(), actions4.toast(`${options.create ? "Created" : "Saved"} ${label2} (${diff.summary})`, "success"), "written") : (actions4.toast(`Write failed. Your edited copy is kept at ${file}`, "error", 12e3), "failed");
6592
6647
  }
6593
- async function readCapped(response, maxBytes) {
6594
- let body = response.body;
6595
- if (!body) return Buffer.alloc(0);
6596
- if (typeof body.getReader != "function") {
6597
- let bytes = Buffer.from(new Uint8Array(await response.arrayBuffer()));
6598
- return bytes.length > maxBytes ? { error: `exceeds ${maxBytes} bytes` } : bytes;
6599
- }
6600
- let reader2 = body.getReader(), chunks = [], total = 0;
6648
+ async function writeOverConflicts(actions4, label2, file, write2, baseRevision) {
6649
+ let base2 = baseRevision;
6601
6650
  for (; ; ) {
6602
- let { done, value } = await reader2.read();
6603
- if (done) break;
6604
- if (value) {
6605
- if (total += value.byteLength, total > maxBytes) {
6606
- try {
6607
- await reader2.cancel();
6608
- } catch {
6609
- }
6610
- return { error: `exceeds ${maxBytes} bytes` };
6611
- }
6612
- chunks.push(Buffer.from(value));
6613
- }
6614
- }
6615
- return Buffer.concat(chunks);
6616
- }
6617
- async function guardedFetch(url2, options) {
6618
- let { maxBytes, timeoutMs } = options, maxRedirects = options.maxRedirects ?? MAX_FETCH_REDIRECTS, daemonPort = daemonGuardPort(), current;
6619
- try {
6620
- current = new URL(url2).toString();
6621
- } catch {
6622
- return { error: `invalid URL: ${String(url2).slice(0, 200)}` };
6623
- }
6624
- let controller = new AbortController(), timer = setTimeout(() => controller.abort(), timeoutMs);
6625
- try {
6626
- for (let hop = 0; ; hop++) {
6627
- let protocol = new URL(current).protocol;
6628
- if (protocol !== "https:")
6629
- return { error: `only https URLs may be fetched (got "${protocol}")` };
6630
- let blocked = await checkFetchTarget(current, { allowLocal: !1, daemonPort });
6631
- if (blocked) return { error: blocked };
6632
- let response = await fetch(current, { signal: controller.signal, redirect: "manual" });
6633
- if (response.status >= 300 && response.status <= 399) {
6634
- let location = response.headers.get("location");
6635
- if (!location) return { error: `redirect without a location header (HTTP ${response.status})` };
6636
- if (hop >= maxRedirects) return { error: `too many redirects (>${maxRedirects})` };
6637
- try {
6638
- await response.body?.cancel();
6639
- } catch {
6640
- }
6641
- try {
6642
- current = new URL(location, current).toString();
6643
- } catch {
6644
- return { error: `redirect to an unusable location: ${location.slice(0, 200)}` };
6645
- }
6646
- continue;
6647
- }
6648
- if (!response.ok) return { error: `HTTP ${response.status} ${response.statusText}`.trim() };
6649
- let lengthHeader = response.headers.get("content-length"), declared = lengthHeader === null ? NaN : Number(lengthHeader);
6650
- if (Number.isFinite(declared) && declared > maxBytes) {
6651
- try {
6652
- await response.body?.cancel();
6653
- } catch {
6654
- }
6655
- return { error: `exceeds ${maxBytes} bytes` };
6656
- }
6657
- let bytes = await readCapped(response, maxBytes);
6658
- return Buffer.isBuffer(bytes) ? { bytes, contentType: response.headers.get("content-type") ?? "" } : bytes;
6659
- }
6660
- } catch (err) {
6661
- let message = err instanceof Error ? err.message : String(err);
6662
- return { error: err instanceof Error && err.name === "AbortError" ? `request timed out after ${timeoutMs}ms` : message };
6663
- } finally {
6664
- clearTimeout(timer);
6665
- }
6666
- }
6667
-
6668
- // src/shared/schemas/skills-catalog.schema.ts
6669
- var skills_catalog_schema_exports = {};
6670
- __export(skills_catalog_schema_exports, {
6671
- MAX_CATALOG_BYTES: () => MAX_CATALOG_BYTES,
6672
- MAX_PACKAGE_FILES: () => MAX_PACKAGE_FILES,
6673
- MAX_SKILL_PACKAGE_BYTES: () => MAX_SKILL_PACKAGE_BYTES,
6674
- SKILL_NAME: () => SKILL_NAME,
6675
- SkillCatalogEntrySchema: () => SkillCatalogEntrySchema,
6676
- parseSkillsCatalogDocument: () => parseSkillsCatalogDocument,
6677
- resolvePackageFilePath: () => resolvePackageFilePath
6678
- });
6679
- var SKILL_NAME = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, PATH_SEGMENT = /^[A-Za-z0-9._-]+$/, MAX_PACKAGE_FILES = 32, MAX_CATALOG_BYTES = 512 * 1024, MAX_SKILL_PACKAGE_BYTES = 256 * 1024, UNSAFE_TEXT = /[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/, safeText = (value) => !UNSAFE_TEXT.test(value), UNSAFE_TEXT_MESSAGE = "must not contain control or bidirectional-override characters";
6680
- function resolvePackageFilePath(name, raw) {
6681
- if (typeof raw != "string") return null;
6682
- let directory = `skills/${name}/`, candidate = raw.trim().replace(/\\/g, "/");
6683
- if (!candidate) return null;
6684
- for (; candidate.startsWith("./"); ) candidate = candidate.slice(2);
6685
- if (candidate.startsWith("/")) return null;
6686
- let relative2 = candidate.startsWith(directory) ? candidate.slice(directory.length) : candidate;
6687
- return !relative2 || relative2.split("/").some((segment) => segment === "." || segment === ".." || !PATH_SEGMENT.test(segment)) || relative2 === "SKILL.md" ? null : relative2;
6688
- }
6689
- var SkillPackageFileSchema = external_exports.object({
6690
- path: external_exports.string().min(1).refine(safeText, UNSAFE_TEXT_MESSAGE),
6691
- raw_url: external_exports.string().url()
6692
- }), SkillCatalogEntrySchema = external_exports.object({
6693
- name: external_exports.string().regex(SKILL_NAME),
6694
- description: external_exports.string().min(1).max(500).refine(safeText, UNSAFE_TEXT_MESSAGE),
6695
- /** Path inside the publishing repo — display only. */
6696
- path: external_exports.string().min(1).refine(safeText, UNSAFE_TEXT_MESSAGE).optional(),
6697
- /** Where the SKILL.md body is fetched from on install. */
6698
- raw_url: external_exports.string().url(),
6699
- /** Resources installed beside SKILL.md, package-relative after parsing. */
6700
- files: external_exports.array(SkillPackageFileSchema).max(MAX_PACKAGE_FILES).optional()
6701
- }).superRefine((entry, ctx) => {
6702
- if (!entry.files) return;
6703
- let seen = /* @__PURE__ */ new Set();
6704
- entry.files.forEach((file, index) => {
6705
- let relative2 = resolvePackageFilePath(entry.name, file.path);
6706
- if (!relative2) {
6707
- ctx.addIssue({
6708
- code: external_exports.ZodIssueCode.custom,
6709
- path: ["files", index, "path"],
6710
- message: `must stay inside skills/${entry.name}/ and cannot be SKILL.md`
6711
- });
6712
- return;
6713
- }
6714
- if (seen.has(relative2)) {
6715
- ctx.addIssue({
6716
- code: external_exports.ZodIssueCode.custom,
6717
- path: ["files", index, "path"],
6718
- message: "duplicate package path"
6719
- });
6720
- return;
6721
- }
6722
- seen.add(relative2);
6723
- });
6724
- }).transform((entry) => entry.files ? {
6725
- ...entry,
6726
- files: entry.files.map((file) => ({
6727
- ...file,
6728
- path: resolvePackageFilePath(entry.name, file.path)
6729
- }))
6730
- } : entry);
6731
- function parseSkillsCatalogDocument(json2) {
6732
- if (typeof json2 != "object" || json2 === null || Array.isArray(json2)) return null;
6733
- let doc = json2;
6734
- if (doc.schema !== void 0 && doc.schema !== 1 || !Array.isArray(doc.skills)) return null;
6735
- let entries = [], seen = /* @__PURE__ */ new Set(), dropped = 0;
6736
- for (let raw of doc.skills) {
6737
- let result = SkillCatalogEntrySchema.safeParse(raw);
6738
- if (!result.success || seen.has(result.data.name)) {
6739
- dropped++;
6740
- continue;
6651
+ let result = await write2(base2);
6652
+ if (!result) return;
6653
+ if ("ok" in result) return !0;
6654
+ let copy = typeof file == "string" ? ` (your edited copy stays at ${file})` : file ? " (your edited version is kept in a file)" : "";
6655
+ if (!await actions4.confirm({
6656
+ title: `${label2} changed while you were editing`,
6657
+ message: `${label2} was ${changedBy(result.stale)} after you opened it.
6658
+ Overwrite replaces their version with yours. Reload keeps theirs${copy}.`,
6659
+ confirmLabel: "Overwrite",
6660
+ cancelLabel: "Reload theirs",
6661
+ danger: !0
6662
+ })) {
6663
+ let kept = typeof file == "function" ? await file().catch(() => null) : file;
6664
+ return actions4.toast(`${label2} not written: kept their version${kept ? `. Your edited copy is at ${kept}` : ""}`, "warn", 12e3), "reloaded";
6741
6665
  }
6742
- seen.add(result.data.name), entries.push(result.data);
6666
+ base2 = result.stale.revision;
6743
6667
  }
6744
- return {
6745
- entries,
6746
- publisher: typeof doc.publisher == "string" ? doc.publisher : void 0,
6747
- repository: typeof doc.repository == "string" ? doc.repository : void 0,
6748
- dropped
6749
- };
6750
6668
  }
6751
6669
 
6752
6670
  // src/main/tui/skills/catalog.ts
6753
- var { guardedFetch: guardedFetch2 } = cjs(guarded_fetch_exports), { parseSkillsCatalogDocument: parseSkillsCatalogDocument2, MAX_CATALOG_BYTES: MAX_CATALOG_BYTES2, MAX_SKILL_PACKAGE_BYTES: MAX_SKILL_PACKAGE_BYTES2 } = cjs(skills_catalog_schema_exports), { normalizeCatalogSources: normalizeCatalogSources2 } = cjs(skills_panel_exports), SKILLS_FETCH_TIMEOUT_MS = 1e4, CATALOG_SOURCES_SETTING = "skillCatalogSources";
6671
+ var { guardedFetch } = cjs(guarded_fetch_exports), { parseSkillsCatalogDocument, MAX_CATALOG_BYTES, MAX_SKILL_PACKAGE_BYTES } = cjs(skills_catalog_schema_exports), { normalizeCatalogSources: normalizeCatalogSources2 } = cjs(skills_panel_exports), SKILLS_FETCH_TIMEOUT_MS = 1e4, CATALOG_SOURCES_SETTING = "skillCatalogSources";
6754
6672
  async function fetchCatalogDocument(url2) {
6755
6673
  if (typeof url2 != "string" || !/^https:\/\//.test(url2)) return { ok: !1, error: "Catalog URL must be https" };
6756
- let body = await guardedFetch2(url2, { maxBytes: MAX_CATALOG_BYTES2, timeoutMs: SKILLS_FETCH_TIMEOUT_MS });
6674
+ let body = await guardedFetch(url2, { maxBytes: MAX_CATALOG_BYTES, timeoutMs: SKILLS_FETCH_TIMEOUT_MS });
6757
6675
  if ("error" in body) return { ok: !1, error: body.error };
6758
6676
  let json2;
6759
6677
  try {
@@ -6761,12 +6679,12 @@ async function fetchCatalogDocument(url2) {
6761
6679
  } catch {
6762
6680
  return { ok: !1, error: "Catalog is not valid JSON" };
6763
6681
  }
6764
- let parsed = parseSkillsCatalogDocument2(json2);
6682
+ let parsed = parseSkillsCatalogDocument(json2);
6765
6683
  return parsed ? { ok: !0, entries: parsed.entries, publisher: parsed.publisher, dropped: parsed.dropped } : { ok: !1, error: "Unrecognized catalog schema" };
6766
6684
  }
6767
6685
  async function fetchSkillText(url2) {
6768
6686
  if (typeof url2 != "string" || !/^https:\/\//.test(url2)) return { ok: !1, error: "Package URL must be https" };
6769
- let body = await guardedFetch2(url2, { maxBytes: MAX_SKILL_PACKAGE_BYTES2, timeoutMs: SKILLS_FETCH_TIMEOUT_MS });
6687
+ let body = await guardedFetch(url2, { maxBytes: MAX_SKILL_PACKAGE_BYTES, timeoutMs: SKILLS_FETCH_TIMEOUT_MS });
6770
6688
  return "error" in body ? { ok: !1, error: body.error } : body.bytes.subarray(0, 8192).includes(0) ? { ok: !1, error: "SKILL.md is not text" } : { ok: !0, content: body.bytes.toString("utf8") };
6771
6689
  }
6772
6690
  var DEFAULT_SEAMS = { catalog: fetchCatalogDocument, text: fetchSkillText }, seams2 = DEFAULT_SEAMS;
@@ -6800,7 +6718,7 @@ var {
6800
6718
  parseSkillsRegistry: parseSkillsRegistry2,
6801
6719
  sanitizeDisplayText: sanitizeDisplayText2,
6802
6720
  skillProblems: skillProblems2
6803
- } = cjs(skills_panel_exports), { splitSkillDocument: splitSkillDocument2 } = cjs(skill_preview_exports), { SKILL_NAME: SKILL_NAME2 } = cjs(skills_catalog_schema_exports), SKILLS_OVERLAY = "skills", looksLikePath = (value) => /^(\.{1,2}([\\/]|$)|[\\/]|~([\\/]|$)|[A-Za-z]:[\\/])/.test(value) || /[\\/]/.test(value) || /\.md$/i.test(value);
6721
+ } = cjs(skills_panel_exports), { splitSkillDocument: splitSkillDocument2 } = cjs(skill_preview_exports), { SKILL_NAME } = cjs(skills_catalog_schema_exports), SKILLS_OVERLAY = "skills", looksLikePath = (value) => /^(\.{1,2}([\\/]|$)|[\\/]|~([\\/]|$)|[A-Za-z]:[\\/])/.test(value) || /[\\/]/.test(value) || /\.md$/i.test(value);
6804
6722
  function parseAddTarget2(raw) {
6805
6723
  let value = (raw ?? "").trim().replace(/^(["'])(.*)\1$/, "$2");
6806
6724
  if (!value) return { kind: "catalog", query: "" };
@@ -6851,7 +6769,7 @@ function settleOverrides(overrides, registryText) {
6851
6769
  function tokenLabel(tokens) {
6852
6770
  return tokens == null ? "" : tokens >= 1e3 ? `~${(tokens / 1e3).toFixed(tokens >= 1e4 ? 0 : 1)}k tok` : `~${tokens} tok`;
6853
6771
  }
6854
- var safe = (value) => sanitizeDisplayText2(value), packageKey = (source) => source.type === "catalog" ? `catalog:${source.entry.sourceUrl}#${source.entry.name}` : source.type === "url" ? `url:${source.url}` : `disk:${source.path}`, frontmatterField = (manifest, key) => splitSkillDocument2(manifest).fields.find((f) => f.key === key)?.value, isSkillName = (value) => !!value && SKILL_NAME2.test(value);
6772
+ var safe = (value) => sanitizeDisplayText2(value), packageKey = (source) => source.type === "catalog" ? `catalog:${source.entry.sourceUrl}#${source.entry.name}` : source.type === "url" ? `url:${source.url}` : `disk:${source.path}`, frontmatterField = (manifest, key) => splitSkillDocument2(manifest).fields.find((f) => f.key === key)?.value, isSkillName = (value) => !!value && SKILL_NAME.test(value);
6855
6773
  function manifestProblem(manifest) {
6856
6774
  return Buffer.byteLength(manifest, "utf-8") > MAX_SKILL_FILE_BYTES2 ? `SKILL.md is over ${Math.round(MAX_SKILL_FILE_BYTES2 / 1024)} KB, the indexer would reject it` : manifest.slice(0, 8192).includes("\0") ? "SKILL.md is not text" : null;
6857
6775
  }
@@ -6919,7 +6837,7 @@ function exactEntry(entries, query) {
6919
6837
  import { promises as fs3 } from "node:fs";
6920
6838
  import { homedir as homedir2 } from "node:os";
6921
6839
  import { basename as basename2, dirname, join as join3, relative, resolve as resolve2, sep } from "node:path";
6922
- var { SKILLS_REGISTRY_PATH: SKILLS_REGISTRY_PATH3, SKILLS_STATE_PATH: SKILLS_STATE_PATH2, mergeDisabledList: mergeDisabledList2, parseSkillsRegistry: parseSkillsRegistry3 } = cjs(skills_panel_exports), { MAX_PACKAGE_FILES: MAX_PACKAGE_FILES2, MAX_SKILL_PACKAGE_BYTES: MAX_SKILL_PACKAGE_BYTES3, resolvePackageFilePath: resolvePackageFilePath2 } = cjs(skills_catalog_schema_exports), errorText2 = (err) => err instanceof Error ? err.message : String(err), isNotFound = (err) => err?.status === 404;
6840
+ var { SKILLS_REGISTRY_PATH: SKILLS_REGISTRY_PATH3, SKILLS_STATE_PATH: SKILLS_STATE_PATH2, mergeDisabledList: mergeDisabledList2, parseSkillsRegistry: parseSkillsRegistry3 } = cjs(skills_panel_exports), { MAX_PACKAGE_FILES, MAX_SKILL_PACKAGE_BYTES: MAX_SKILL_PACKAGE_BYTES2, resolvePackageFilePath } = cjs(skills_catalog_schema_exports), errorText2 = (err) => err instanceof Error ? err.message : String(err), isNotFound = (err) => err?.status === 404;
6923
6841
  async function must(result, what) {
6924
6842
  let outcome = await result;
6925
6843
  if (outcome && outcome.success === !1) throw new Error(`${what} was refused (protected file?)`);
@@ -7047,7 +6965,7 @@ async function readLocalPackage(input, cwd = process.cwd()) {
7047
6965
  try {
7048
6966
  let manifestStat = await fs3.stat(manifestPath);
7049
6967
  if (!manifestStat.isFile()) return { ok: !1, error: `${manifestPath} is not a file` };
7050
- if (manifestStat.size > MAX_SKILL_PACKAGE_BYTES3) return { ok: !1, error: `SKILL.md is over ${Math.round(MAX_SKILL_PACKAGE_BYTES3 / 1024)} KB` };
6968
+ if (manifestStat.size > MAX_SKILL_PACKAGE_BYTES2) return { ok: !1, error: `SKILL.md is over ${Math.round(MAX_SKILL_PACKAGE_BYTES2 / 1024)} KB` };
7051
6969
  manifestBytes = await fs3.readFile(manifestPath);
7052
6970
  } catch {
7053
6971
  return { ok: !1, error: stat.isDirectory() ? `No SKILL.md in ${target2}` : `Cannot read ${target2}` };
@@ -7079,24 +6997,24 @@ async function readLocalPackage(input, cwd = process.cwd()) {
7079
6997
  if (!entry.isFile()) continue;
7080
6998
  let rel = relative(target2, full).split(sep).join("/");
7081
6999
  if (rel === "SKILL.md") continue;
7082
- let path = resolvePackageFilePath2(identity.name, rel);
7000
+ let path = resolvePackageFilePath(identity.name, rel);
7083
7001
  if (!path) {
7084
7002
  warnings.push(`${rel}: skipped (not a package path)`);
7085
7003
  continue;
7086
7004
  }
7087
- if (resources.length >= MAX_PACKAGE_FILES2) {
7005
+ if (resources.length >= MAX_PACKAGE_FILES) {
7088
7006
  skippedOver++;
7089
7007
  continue;
7090
7008
  }
7091
- if ((await fs3.stat(full)).size > MAX_SKILL_PACKAGE_BYTES3) {
7092
- warnings.push(`${rel}: skipped (over ${Math.round(MAX_SKILL_PACKAGE_BYTES3 / 1024)} KB)`);
7009
+ if ((await fs3.stat(full)).size > MAX_SKILL_PACKAGE_BYTES2) {
7010
+ warnings.push(`${rel}: skipped (over ${Math.round(MAX_SKILL_PACKAGE_BYTES2 / 1024)} KB)`);
7093
7011
  continue;
7094
7012
  }
7095
7013
  let bytes = await fs3.readFile(full);
7096
7014
  resources.push(bytes.subarray(0, 8192).includes(0) ? { path, contentBase64: bytes.toString("base64") } : { path, content: bytes.toString("utf-8") });
7097
7015
  }
7098
7016
  };
7099
- await walk(target2, 0), skippedOver && warnings.push(`${skippedOver} more file${skippedOver === 1 ? "" : "s"} skipped (a package ships at most ${MAX_PACKAGE_FILES2} beside SKILL.md)`);
7017
+ await walk(target2, 0), skippedOver && warnings.push(`${skippedOver} more file${skippedOver === 1 ? "" : "s"} skipped (a package ships at most ${MAX_PACKAGE_FILES} beside SKILL.md)`);
7100
7018
  }
7101
7019
  return {
7102
7020
  ok: !0,
@@ -7425,7 +7343,7 @@ function SkillDoc({ manifest, width, height }) {
7425
7343
  var import_react24 = __toESM(require_react(), 1);
7426
7344
 
7427
7345
  // src/main/tui/context/model.ts
7428
- var { resolveLoopThreshold } = cjs(context_breakdown_exports), CONTEXT_OVERLAY = "context";
7346
+ var { resolveLoopThreshold, calibrationNote } = cjs(context_breakdown_exports), CONTEXT_OVERLAY = "context";
7429
7347
  function openContext(store, props) {
7430
7348
  store.actions.pushOverlay({ kind: CONTEXT_OVERLAY, props: { ...props } });
7431
7349
  }
@@ -7448,6 +7366,9 @@ var SOURCE_TEXT = {
7448
7366
  function thresholdText(r, fmt) {
7449
7367
  return `auto-compacts at ${fmt(r.compactThreshold)} (${SOURCE_TEXT[r.compactThresholdSource]})`;
7450
7368
  }
7369
+ function calibrationText(cal, approx, fmt) {
7370
+ return calibrationNote(cal, fmt, approx);
7371
+ }
7451
7372
  function contextRows(categories, expanded) {
7452
7373
  let rows = [];
7453
7374
  for (let category of categories) {
@@ -7594,7 +7515,7 @@ function ContextDialog({ overlay, close: closeOverlay, width, height }) {
7594
7515
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Box_default, { marginTop: 1, flexDirection: "column", height: Math.min(rows.length, rowsHeight), children: shown.map((row4, i) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RowLine, { row: row4, selectedId: selected?.id, total, labelWidth, rowBar, theme: theme2 }, `${start + i}`)) }),
7595
7516
  rows.length > rowsHeight ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { color: theme2.color.dim, children: `${start + 1}-${Math.min(rows.length, start + rowsHeight)} of ${rows.length} rows` }) : null,
7596
7517
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { color: theme2.color.muted, wrap: "truncate-end", children: selected ? `${selected.label}: ${selected.note}` : "" }) }),
7597
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { color: theme2.color.dim, wrap: "truncate-end", children: s.compacting ? `Compacting ${s.loop}${g.ellipsis}` : s.loading ? `Re-measuring${g.ellipsis}` : s.measuredAt ? `Measured ${formatClock(s.measuredAt)} ${g.sep} conversation estimated, the rest tokenized` : "" })
7518
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { color: theme2.color.dim, wrap: "truncate-end", children: s.compacting ? `Compacting ${s.loop}${g.ellipsis}` : s.loading ? `Re-measuring${g.ellipsis}` : s.measuredAt ? `Measured ${formatClock(s.measuredAt)} ${g.sep} ${calibrationText(s.data?.breakdown?.calibration, g.approx, formatCount)}` : "" })
7598
7519
  ] });
7599
7520
  }
7600
7521
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Modal, { title: `Context ${g.sep} ${truncate(header2, inner - 12)}`, width: dialogWidth, hints, onClose: close, children: body });
@@ -7602,7 +7523,7 @@ function ContextDialog({ overlay, close: closeOverlay, width, height }) {
7602
7523
  function RowLine({ row: row4, selectedId, total, labelWidth, rowBar, theme: theme2 }) {
7603
7524
  let g = theme2.glyph, share = (tokens) => total > 0 ? `${Math.round(tokens / total * 100)}%` : "0%";
7604
7525
  if (row4.kind === "category") {
7605
- let c = row4.category, isSel = c.id === selectedId, label2 = `${row4.expandable ? row4.expanded ? g.expanded : g.collapsed : " "} ${c.label}${c.count !== void 0 ? ` (${c.count})` : ""}`, bar = barText(c.tokens, total, rowBar, theme2.ascii);
7526
+ let c = row4.category, isSel = c.id === selectedId, label2 = `${row4.expandable ? row4.expanded ? g.expanded : g.collapsed : " "} ${c.label}${c.count !== void 0 ? ` (${c.count})` : ""}${c.basis !== "measured" ? ` ${g.approx}` : ""}`, bar = barText(c.tokens, total, rowBar, theme2.ascii);
7606
7527
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Text, { wrap: "truncate-end", inverse: theme2.mono && isSel, bold: isSel, children: [
7607
7528
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Text, { color: isSel ? theme2.color.accent : theme2.color.dim, children: [
7608
7529
  isSel ? g.pointer : " ",
@@ -8013,7 +7934,7 @@ var RUNTIME_VIEW = "runtime", RUNTIME_TABS = [
8013
7934
  { id: "status", title: "Status", description: "Daemon health, version, uptime and every agent\u2019s loops" },
8014
7935
  { id: "folders", title: "Folders", description: "Tracked agent folders: Enter its agents (review, load, errors), a add, d stop tracking, r rescan" },
8015
7936
  { id: "identity", title: "Identity", description: "Owner identity: status; Enter to create, restore, unlock or lock" },
8016
- { id: "auth", title: "Sign-in", description: "Provider sign-ins (ChatGPT, Grok) and API-key providers; Enter to sign in" },
7937
+ { id: "auth", title: "Sign-in", description: "Provider sign-ins (ChatGPT, Grok, Claude) and API-key providers; Enter to sign in" },
8017
7938
  { id: "providers", title: "Providers", description: "LLM providers and which agents use them" },
8018
7939
  { id: "usage", title: "Usage", description: "Token usage across agents, by model" },
8019
7940
  { id: "network", title: "Network", description: "Mesh, LAN, WebSocket; m mesh on/off, s server start/stop" },
@@ -8488,14 +8409,14 @@ var REPORTS = {
8488
8409
  title: "Auth",
8489
8410
  async load(client) {
8490
8411
  let d = await client.authStatus(), lines = [heading("Subscription sign-ins")];
8491
- for (let [name, status] of [["chatgpt", d.chatgpt], ["grok", d.grok]]) {
8492
- let s = status ?? {};
8493
- lines.push(indent([{ text: name.padEnd(9), tone: "key" }, s.authenticated ? { text: "signed in", tone: "success" } : { text: "not signed in", tone: "muted" }, { text: s.email ? ` ${String(s.email)}` : "", tone: "muted" }]));
8412
+ for (let [name, status] of [["chatgpt", d.chatgpt], ["grok", d.grok], ["claude", d.claude]]) {
8413
+ let s = status ?? {}, state = s.authenticated ? { text: "signed in", tone: "success" } : s.installed === !1 ? { text: "Claude Code not installed", tone: "warn" } : { text: "not signed in", tone: "muted" }, extra = [s.email ? String(s.email) : "", s.plan ? `${String(s.plan)} plan` : "", s.installed === !1 && s.installUrl ? String(s.installUrl) : ""].filter(Boolean).join(" \xB7 ");
8414
+ lines.push(indent([{ text: name.padEnd(9), tone: "key" }, state, { text: extra ? ` ${extra}` : "", tone: "muted" }]));
8494
8415
  }
8495
8416
  return lines.push(blank(), heading("API-key providers")), lines.push(...tableLines(
8496
8417
  ["id", "type", "key", "stored in"],
8497
8418
  d.providers.map((p) => [p.id, p.type, p.hasApiKey ? "set" : "missing", p.credentialStorage])
8498
- ).map(indent)), lines.push(blank(), heading("Sign in or out")), lines.push(indent(plain("Enter opens the sign-in dialog \xB7 /login chatgpt \xB7 /login grok \xB7 /logout <chatgpt|grok>", "accent"))), lines.push(indent(plain("From a shell: adf auth login <chatgpt|grok>", "muted"))), lines.push(indent(plain("API keys: Studio settings, stored by the daemon (never shown here).", "muted"))), { data: redactSecrets(d), lines };
8419
+ ).map(indent)), lines.push(blank(), heading("Sign in or out")), lines.push(indent(plain("Enter opens the sign-in dialog \xB7 /login chatgpt \xB7 /login grok \xB7 /login claude \xB7 /logout <chatgpt|grok|claude>", "accent"))), lines.push(indent(plain("From a shell: adf auth login <chatgpt|grok|claude>", "muted"))), lines.push(indent(plain("API keys: Studio settings, stored by the daemon (never shown here).", "muted"))), { data: redactSecrets(d), lines };
8499
8420
  }
8500
8421
  },
8501
8422
  network: {
@@ -8902,15 +8823,14 @@ function folderAgentsSummary(agents) {
8902
8823
  }
8903
8824
  async function loadFolderAgent(store, agent2, options = {}) {
8904
8825
  try {
8905
- options.accept && await store.client.acceptReview(agent2.filePath);
8906
- let ref = await store.client.load(agent2.filePath, !0), name = ref.config?.handle || ref.config?.name || agent2.name, started = "";
8826
+ let accepted = options.accept ? await store.client.acceptReview(agent2.filePath) : null, ref = await store.client.load(agent2.filePath, !0), name = ref.config?.handle || ref.config?.name || agent2.name, started = "";
8907
8827
  if (agent2.autostart)
8908
8828
  try {
8909
8829
  await store.client.start(ref.id), started = " and started it";
8910
8830
  } catch (err) {
8911
8831
  started = ` (start failed: ${err instanceof Error ? err.message : String(err)})`;
8912
8832
  }
8913
- return store.actions.toast(`${options.accept ? "Reviewed and loaded" : "Loaded"} ${name}${started}`, started.startsWith(" (") ? "warn" : "success", 5e3), writeFolders(store, {}, !0), await store.actions.refreshAgents(), store.actions.refreshTracked(), { ok: !0, agentId: ref.id };
8833
+ return store.actions.toast(`${options.accept ? "Reviewed and loaded" : "Loaded"} ${name}${started}${lockedNote(accepted?.lockedFields)}`, started.startsWith(" (") ? "warn" : "success", 6e3), writeFolders(store, {}, !0), await store.actions.refreshAgents(), store.actions.refreshTracked(), { ok: !0, agentId: ref.id };
8914
8834
  } catch (err) {
8915
8835
  err instanceof DaemonError && err.unreachable && store.dispatch({ type: "daemon/reachable", reachable: !1 });
8916
8836
  let error = err instanceof DaemonError && err.status === 403 ? "It needs review first." : err instanceof Error ? err.message : String(err);
@@ -10109,7 +10029,10 @@ function ItemBody({ item, width, expanded, showThinking, queued }) {
10109
10029
  g.dot,
10110
10030
  " "
10111
10031
  ] }),
10112
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Box_default, { width: w - 2, flexDirection: "column", children: item.text ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Markdown, { text: item.text, color: theme2.color.assistant }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { children: " " }) })
10032
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { width: w - 2, flexDirection: "column", children: [
10033
+ item.text ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Markdown, { text: item.text, color: theme2.color.assistant }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { children: " " }),
10034
+ item.interrupted ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { color: theme2.color.dim, children: "[interrupted]" }) : null
10035
+ ] })
10113
10036
  ] });
10114
10037
  case "thinking": {
10115
10038
  let open2 = expanded || showThinking, lines = lineCount(item.text);
@@ -10133,7 +10056,7 @@ function ItemBody({ item, width, expanded, showThinking, queued }) {
10133
10056
  case "tool":
10134
10057
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ToolBody, { item, w, expanded });
10135
10058
  case "hil": {
10136
- let tone = item.status === "pending" ? theme2.color.warn : item.status === "approved" ? theme2.color.success : theme2.color.error;
10059
+ let tone = item.status === "pending" ? theme2.color.warn : item.status === "approved" || item.status === "completed" ? theme2.color.success : theme2.color.error;
10137
10060
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", width: w, borderStyle: theme2.ascii ? "classic" : "single", borderLeft: !0, borderRight: !1, borderTop: !1, borderBottom: !1, borderColor: tone, paddingLeft: 1, children: [
10138
10061
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", children: [
10139
10062
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { bold: !0, color: tone, children: [
@@ -11665,7 +11588,7 @@ var import_react38 = __toESM(require_react(), 1);
11665
11588
  import { promises as fs4, rmSync, statSync as statSync3 } from "node:fs";
11666
11589
  import { tmpdir as tmpdir3 } from "node:os";
11667
11590
  import { join as join4 } from "node:path";
11668
- var copies = /* @__PURE__ */ new Map(), dirs = /* @__PURE__ */ new Set(), exitHook = !1, copyKey = (agentId, target2) => `${agentId}\0${targetKey(target2)}`, errorText5 = (err) => err instanceof Error ? err.message : String(err), bytesOf = (loaded) => loaded.binary ? Buffer.from(loaded.bytes ?? new Uint8Array()) : Buffer.from(loaded.text ?? "", "utf-8");
11591
+ var { openBrowser: openBrowser2 } = cjs(auth_flow_exports), copies = /* @__PURE__ */ new Map(), dirs = /* @__PURE__ */ new Set(), exitHook = !1, copyKey = (agentId, target2) => `${agentId}\0${targetKey(target2)}`, errorText5 = (err) => err instanceof Error ? err.message : String(err), bytesOf = (loaded) => loaded.binary ? Buffer.from(loaded.bytes ?? new Uint8Array()) : Buffer.from(loaded.text ?? "", "utf-8");
11669
11592
  function externalCopy(agentId, target2) {
11670
11593
  return copies.get(copyKey(agentId, target2));
11671
11594
  }
@@ -11696,7 +11619,7 @@ async function openExternal(deps, target2) {
11696
11619
  }
11697
11620
  copies.set(copyKey(agentId, target2), { agentId, target: target2, file, original: bytes, binary: loaded.binary, mime: loaded.mime, mtimeMs: (await fs4.stat(file)).mtimeMs });
11698
11621
  }
11699
- return (deps.open ?? openBrowser)(file), actions4.toast(`Opened ${label2} in the default app \u2014 after saving there, press s to save it back (${file})`, "info", 8e3), !0;
11622
+ return (deps.open ?? openBrowser2)(file), actions4.toast(`Opened ${label2} in the default app \u2014 after saving there, press s to save it back (${file})`, "info", 8e3), !0;
11700
11623
  }
11701
11624
  async function saveBackExternal(deps, target2) {
11702
11625
  let { client, actions: actions4, agentId, agentLabel: agentLabel5 } = deps, label2 = targetLabel(target2), copy = externalCopy(agentId, target2);
@@ -11710,14 +11633,14 @@ async function saveBackExternal(deps, target2) {
11710
11633
  }
11711
11634
  if (edited.equals(copy.original))
11712
11635
  return copy.mtimeMs = (await fs4.stat(copy.file)).mtimeMs, actions4.toast(`No changes to ${label2}`, "info"), "unchanged";
11713
- let concurrent = null;
11636
+ let concurrent = null, baseRevision;
11714
11637
  try {
11715
11638
  let current = await loadTarget(client, agentId, target2);
11716
- bytesOf(current).equals(copy.original) || (concurrent = "changed in the agent since you opened it");
11639
+ baseRevision = current.revision, bytesOf(current).equals(copy.original) || (concurrent = "changed in the agent since you opened it");
11717
11640
  } catch (err) {
11718
11641
  concurrent = err.status === 404 ? "was deleted since you opened it" : `could not be re-checked (${errorText5(err)})`;
11719
11642
  }
11720
- return await actions4.confirm({
11643
+ if (!await actions4.confirm({
11721
11644
  title: concurrent ? `Concurrent change: ${label2}` : `Save ${label2}`,
11722
11645
  message: [
11723
11646
  concurrent ? `WARNING: ${label2} ${concurrent}. Writing replaces that version.` : null,
@@ -11727,7 +11650,9 @@ async function saveBackExternal(deps, target2) {
11727
11650
  confirmLabel: concurrent ? "Overwrite" : "Write",
11728
11651
  cancelLabel: "Cancel",
11729
11652
  danger: !!concurrent
11730
- }) ? await actions4.run(`Write ${label2}`, (c) => (copy.binary && target2.kind === "file" ? c.writeFile(agentId, target2.path, { contentBase64: edited.toString("base64"), ...copy.mime ? { mimeType: copy.mime } : {} }) : writeTarget(c, agentId, target2, edited.toString("utf-8"))).then(() => !0)) ? (copy.original = edited, copy.mtimeMs = (await fs4.stat(copy.file)).mtimeMs, actions4.toast(`Saved ${label2} from the app`, "success"), "written") : (actions4.toast(`Write failed. Your copy is still at ${copy.file}`, "error", 12e3), "failed") : "discarded";
11653
+ })) return "discarded";
11654
+ let written = copy.binary && target2.kind === "file" ? await actions4.run(`Write ${label2}`, (c) => c.writeFile(agentId, target2.path, { contentBase64: edited.toString("base64"), ...copy.mime ? { mimeType: copy.mime } : {} }).then(() => !0)) : await writeOverConflicts(actions4, label2, copy.file, (base2) => actions4.run(`Write ${label2}`, (c) => writeOver(() => writeTarget(c, agentId, target2, edited.toString("utf-8"), base2))), baseRevision);
11655
+ return written === "reloaded" ? "discarded" : written ? (copy.original = edited, copy.mtimeMs = (await fs4.stat(copy.file)).mtimeMs, actions4.toast(`Saved ${label2} from the app`, "success"), "written") : (actions4.toast(`Write failed. Your copy is still at ${copy.file}`, "error", 12e3), "failed");
11731
11656
  }
11732
11657
 
11733
11658
  // src/main/tui/views/files/FilesTab.tsx
@@ -14560,8 +14485,8 @@ var TOOL_GROUPS = [
14560
14485
  off: "nobody: no inbound messages, sends still allowed",
14561
14486
  directory: "only ancestor-directory agents on this runtime",
14562
14487
  localhost: "any agent on this machine",
14563
- lan: "any agent on the local network (binds 0.0.0.0)",
14564
- public: "any agent reachable over the public internet (binds 0.0.0.0)"
14488
+ lan: "any agent on an allowed network",
14489
+ public: "any agent that reaches this computer, also only on an allowed network"
14565
14490
  }, SEND_MODES = ["proactive", "respond_only", "listen_only"], SEND_MODE_HINTS = {
14566
14491
  proactive: "can send anytime",
14567
14492
  respond_only: "sends only when a message triggered the turn",
@@ -14573,8 +14498,6 @@ function triState(items, pred) {
14573
14498
  return n === 0 ? "none" : n === items.length ? "all" : "mixed";
14574
14499
  }
14575
14500
  function groupBlocked(config2, groupId) {
14576
- let triggers = config2.triggers;
14577
- if (groupId === "messaging" && !triggers?.on_inbox?.enabled) return "needs messaging (the on_inbox trigger)";
14578
14501
  if (groupId === "inbox" && config2.messaging?.inbox_mode !== !0) return "needs inbox mode (Settings \u203A Inbox mode)";
14579
14502
  }
14580
14503
  function mcpServers(config2) {
@@ -14731,6 +14654,26 @@ function setMainThreshold(config2, value) {
14731
14654
  let next = clone(config2), context = { ...next.context ?? {} };
14732
14655
  return value === null ? delete context.compact_threshold : context.compact_threshold = value, next.context = context, { ok: !0, config: next, message: value === null ? `main compacts at the default (${formatTokens2(mainThreshold(next).value)})` : `main compacts at ${formatTokens2(value)} tokens` };
14733
14656
  }
14657
+ function requestMediaLimitOf(config2) {
14658
+ let files2 = config2.context?.max_request_media_files, mb = config2.context?.max_request_media_mb;
14659
+ return {
14660
+ files: typeof files2 == "number" && files2 > 0 ? files2 : 20,
14661
+ mb: typeof mb == "number" && mb > 0 ? mb : 20,
14662
+ set: typeof files2 == "number" && files2 > 0 || typeof mb == "number" && mb > 0
14663
+ };
14664
+ }
14665
+ function setRequestMediaLimit(config2, value) {
14666
+ let next = clone(config2), context = { ...next.context ?? {} };
14667
+ delete context.max_request_media_files, delete context.max_request_media_mb, value && (context.max_request_media_files = value.files, context.max_request_media_mb = value.mb), next.context = context;
14668
+ let now = requestMediaLimitOf(next);
14669
+ return { ok: !0, config: next, message: `Requests carry at most ${now.files} files, ${now.mb} MB${value ? "" : " (default)"}` };
14670
+ }
14671
+ function parseMediaLimit(args) {
14672
+ if (args.length === 1 && args[0].toLowerCase() === "default") return { ok: !0, value: null };
14673
+ if (args.length !== 2) return { ok: !1, error: "Give files and MB, or default" };
14674
+ let [files2, mb] = args.map((a) => Number(a));
14675
+ return [files2, mb].every((n) => Number.isInteger(n) && n > 0) ? { ok: !0, value: { files: files2, mb } } : { ok: !1, error: "Files and MB must be positive whole numbers" };
14676
+ }
14734
14677
  function parseThreshold(text) {
14735
14678
  let raw = text.trim().toLowerCase().replace(/[,_\s]/g, "");
14736
14679
  if (!raw || raw === "default" || raw === "inherit" || raw === "0") return { ok: !0, value: null };
@@ -14837,6 +14780,15 @@ function settingRows(config2, extras = {}) {
14837
14780
  value: `main at ${formatTokens2(main.value)}${main.source === "default" ? " (default)" : ""}${typeof used == "number" ? ` \xB7 in use ${formatTokens2(used)} (${Math.round(used / main.value * 100)}%)` : ""}`,
14838
14781
  hint: (config2.loops ?? []).length ? `inner loops: ${(config2.loops ?? []).map((l) => `${l.name} ${l.compact_threshold != null ? formatTokens2(l.compact_threshold) : "inherit"}`).join(", ")}` : "History is summarized when the context reaches this size",
14839
14782
  ...lock(["context"])
14783
+ });
14784
+ let media2 = requestMediaLimitOf(config2);
14785
+ rows.push({
14786
+ id: "media",
14787
+ label: "Media",
14788
+ kind: "dialog",
14789
+ value: `${media2.files} files, ${media2.mb} MB per request${media2.set ? "" : " (default)"}`,
14790
+ hint: "Past either, the oldest files are left out until half remains \xB7 /media <files> <MB> | default",
14791
+ ...lock(["context"])
14840
14792
  }), rows.push({
14841
14793
  id: "autonomous",
14842
14794
  label: "Autonomous",
@@ -14931,6 +14883,9 @@ async function actOnSetting(actions4, agentId, row4, config2) {
14931
14883
  case "compaction":
14932
14884
  actions4.pushOverlay({ kind: COMPACTION_OVERLAY, props: { agentId } });
14933
14885
  return;
14886
+ case "media":
14887
+ actions4.toast("Set with /media <files> <MB>, or /media default", "info", 3e3);
14888
+ return;
14934
14889
  case "visibility":
14935
14890
  case "send":
14936
14891
  actions4.pushOverlay({ kind: CHOICE_OVERLAY, props: { agentId, setting: row4.id } });
@@ -15304,6 +15259,24 @@ var inspect = {
15304
15259
  }
15305
15260
  await applyThreshold(ctx.store, ctx.agentId, ctx.loop, parsed.value);
15306
15261
  }
15262
+ }, media = {
15263
+ name: "media",
15264
+ args: "[<files> <MB>|default]",
15265
+ description: "Request media limit of the selected agent: most files and MB one request carries (default 20, 20). Past either, the oldest are left out until half remains",
15266
+ available: (scope) => !!scope.agentId,
15267
+ complete: (partial) => ["default"].filter((v) => v.startsWith(partial.trim())),
15268
+ run: async (ctx) => {
15269
+ if (!ctx.agentId) {
15270
+ ctx.print("No agent selected", "warn");
15271
+ return;
15272
+ }
15273
+ let parsed = parseMediaLimit(ctx.args);
15274
+ if (!parsed.ok) {
15275
+ ctx.print(`${parsed.error}. Usage: /media <files> <MB> | default`, "warn");
15276
+ return;
15277
+ }
15278
+ await applyChange(ctx.actions, ctx.agentId, (cfg) => setRequestMediaLimit(cfg, parsed.value), "Media");
15279
+ }
15307
15280
  }, TASK_FILTER_ARGS = TASK_FILTERS, tasks = {
15308
15281
  name: "tasks",
15309
15282
  args: `[${TASK_FILTERS.join("|")}]`,
@@ -15356,7 +15329,7 @@ var inspect = {
15356
15329
  patchInspectState(ctx.actions, ctx.state(), { tab: t.id }), ctx.actions.setView(INSPECT_VIEW);
15357
15330
  }
15358
15331
  }))
15359
- ], inspectCommands = { commands: [inspect, model, config, tasks, instructions, tools, compaction], actions: actions2 };
15332
+ ], inspectCommands = { commands: [inspect, model, config, tasks, instructions, tools, compaction, media], actions: actions2 };
15360
15333
 
15361
15334
  // src/main/tui/views/inspect/EventsTab.tsx
15362
15335
  var import_react51 = __toESM(require_react(), 1);
@@ -15615,6 +15588,9 @@ function typeColor(theme2, type) {
15615
15588
  // src/main/tui/views/inspect/ConfigTab.tsx
15616
15589
  var import_react52 = __toESM(require_react(), 1);
15617
15590
  var import_jsx_runtime64 = __toESM(require_jsx_runtime(), 1);
15591
+ import { promises as fs5 } from "node:fs";
15592
+ import { tmpdir as tmpdir4 } from "node:os";
15593
+ import { join as join5 } from "node:path";
15618
15594
  async function editConfigFlow(agentId, label2, actions4, suspend, edit = (text) => editText(text, { filename: `${label2}.config.json` })) {
15619
15595
  let original = await actions4.loadConfig(agentId);
15620
15596
  if (!original) return "failed";
@@ -15646,10 +15622,10 @@ Edit again? Discard drops your edit.`,
15646
15622
  }
15647
15623
  if (check.changedKeys.length === 0)
15648
15624
  return actions4.toast("Config unchanged (formatting only), nothing saved", "info"), "unchanged";
15649
- let fresh = await actions4.run("Re-check config", (c) => c.config(agentId).then((r) => r.config));
15650
- if (!fresh) return "failed";
15651
- let theirs = changedKeysBetween(original, fresh);
15652
- return theirs.length > 0 && !await actions4.confirm({
15625
+ let freshResult = await actions4.run("Re-check config", (c) => c.config(agentId));
15626
+ if (!freshResult) return "failed";
15627
+ let fresh = freshResult.config, baseRevision = revisionOf(freshResult), theirs = changedKeysBetween(original, fresh);
15628
+ if (theirs.length > 0 && !await actions4.confirm({
15653
15629
  title: `Config of ${label2} changed on the daemon`,
15654
15630
  message: `While you were editing, these keys changed on the daemon: ${theirs.join(", ")}.
15655
15631
  Saving your copy reverts them${theirs.includes("loops") ? " (a loop added meanwhile would be removed and archived)" : ""}.
@@ -15658,14 +15634,24 @@ Warning: ${check.warnings.join("; ")}` : ""}`,
15658
15634
  confirmLabel: "Overwrite",
15659
15635
  cancelLabel: "Discard",
15660
15636
  danger: !0
15661
- }) ? (actions4.toast("Config edit discarded (the daemon copy changed meanwhile)", "warn"), "discarded") : changedKeysBetween(fresh, check.config).length === 0 ? (actions4.toast("Config already matches the daemon, nothing saved", "info"), "unchanged") : theirs.length > 0 || await actions4.confirm({
15637
+ }))
15638
+ return actions4.toast("Config edit discarded (the daemon copy changed meanwhile)", "warn"), "discarded";
15639
+ if (changedKeysBetween(fresh, check.config).length === 0)
15640
+ return actions4.toast("Config already matches the daemon, nothing saved", "info"), "unchanged";
15641
+ if (!(theirs.length > 0 || await actions4.confirm({
15662
15642
  title: `Save config of ${label2}?`,
15663
15643
  message: `Changed: ${check.changedKeys.join(", ")}${check.warnings.length ? `
15664
15644
  Warning: ${check.warnings.join("; ")}` : ""}`,
15665
15645
  confirmLabel: "Save",
15666
15646
  cancelLabel: "Discard",
15667
15647
  danger: check.warnings.length > 0
15668
- }) ? await actions4.run("Save config", (c) => c.putConfig(agentId, check.config)) ? (actions4.toast(`Config of ${label2} saved (${check.changedKeys.join(", ")})`, "success"), await actions4.loadConfig(agentId), "saved") : "failed" : (actions4.toast("Config edit discarded", "warn"), "discarded");
15648
+ })))
15649
+ return actions4.toast("Config edit discarded", "warn"), "discarded";
15650
+ let keepCopy = async () => {
15651
+ let file = join5(tmpdir4(), `adf-tui-${label2.replace(/[^\w.-]+/g, "_")}.config-${Date.now()}.json`);
15652
+ return await fs5.writeFile(file, result.text ?? "", "utf-8"), file;
15653
+ }, saved = await writeOverConflicts(actions4, `Config of ${label2}`, keepCopy, (base2) => actions4.run("Save config", (c) => writeOver(() => c.request("PUT", `/agents/${encodeURIComponent(agentId)}/config`, { body: check.config, ...base2 !== void 0 ? { query: { baseRevision: base2 } } : {} }))), baseRevision);
15654
+ return saved === "reloaded" ? (await actions4.loadConfig(agentId), "discarded") : saved ? (actions4.toast(`Config of ${label2} saved (${check.changedKeys.join(", ")})`, "success"), await actions4.loadConfig(agentId), "saved") : "failed";
15669
15655
  }
15670
15656
  }
15671
15657
  function ConfigTab({ agentId, label: label2, width, height, focused }) {