zelari-code 1.47.2 → 1.48.1

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.
@@ -20,6 +20,7 @@ import { startPermissionBroker, } from "../mcp/permissionBroker.js";
20
20
  import { createBrokerPermissionHandler, createBrokerQuestionHandler, } from "../mcp/brokerHandlers.js";
21
21
  import { createPermissionAskHandler } from "./permissionPicker.js";
22
22
  import { appendSystem } from "./messageHelpers.js";
23
+ import { armPickerTimeout, askUserTimeoutMs } from "./askUserTimeout.js";
23
24
  export function usePermissionBroker(opts) {
24
25
  const { setPicker, setMessages } = opts;
25
26
  const handleRef = useRef(null);
@@ -42,13 +43,22 @@ export function usePermissionBroker(opts) {
42
43
  return;
43
44
  }
44
45
  let settled = false;
46
+ let cancelWaitTimeout;
45
47
  const finish = (value) => {
46
48
  if (settled)
47
49
  return;
48
50
  settled = true;
51
+ cancelWaitTimeout?.();
49
52
  setPicker(null);
50
53
  resolve(value);
51
54
  };
55
+ // v1.47.x: timeout so an unseen external-agent question cannot block
56
+ // the broker client forever. Knob: ZELARI_ASK_USER_TIMEOUT_MS.
57
+ const waitTimeoutMs = askUserTimeoutMs();
58
+ cancelWaitTimeout = armPickerTimeout(() => {
59
+ appendSystem(setMessages, `[external agent] nessuna risposta entro ${Math.round(waitTimeoutMs / 1000)}s — richiesta annullata.`, Date.now());
60
+ finish(null);
61
+ }, waitTimeoutMs);
52
62
  appendSystem(setMessages, `[external agent] ${req.question}\n→ ${choices.join(" · ")}`, Date.now());
53
63
  setPicker({
54
64
  kind: "clarification",
@@ -1 +1 @@
1
- {"version":3,"file":"usePermissionBroker.js","sourceRoot":"","sources":["../../../src/cli/hooks/usePermissionBroker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usePermissionBroker.js","sourceRoot":"","sources":["../../../src/cli/hooks/usePermissionBroker.ts"],"names":[],"mappings":"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"}
@@ -628,7 +628,12 @@ async function refreshGrokToken(options) {
628
628
  grant_type: "refresh_token",
629
629
  client_id: options.clientId,
630
630
  refresh_token: options.refreshToken
631
- }).toString()
631
+ }).toString(),
632
+ // v1.47.x: hard 30s cap. A hung auth socket used to freeze the whole
633
+ // turn on "working" forever with no error (the refresh promise never
634
+ // settled). A visible failure is strictly better: callers fall back
635
+ // to the current token and surface the error.
636
+ signal: AbortSignal.timeout(3e4)
632
637
  });
633
638
  } catch (err) {
634
639
  throw new GrokOAuthError(
@@ -29126,6 +29131,7 @@ __export(openai_compatible_exports, {
29126
29131
  parseCachedPromptTokens: () => parseCachedPromptTokens,
29127
29132
  providerConfigFor: () => providerConfigFor,
29128
29133
  providerFromEnv: () => providerFromEnv,
29134
+ readChunkWithTimeout: () => readChunkWithTimeout,
29129
29135
  resolveActiveProvider: () => resolveActiveProvider,
29130
29136
  resolveBaseUrl: () => resolveBaseUrl
29131
29137
  });
@@ -29151,13 +29157,20 @@ async function readChunkWithTimeout(reader, opts) {
29151
29157
  if (opts.signal?.aborted) {
29152
29158
  throw new Error("aborted");
29153
29159
  }
29154
- const remaining = opts.deadlineMs - Date.now();
29160
+ const now = Date.now();
29161
+ const remaining = opts.deadlineMs - now;
29155
29162
  if (remaining <= 0) {
29156
29163
  throw new Error(
29157
29164
  `Provider stream exceeded max duration (${Math.round(PROVIDER_STREAM_MAX_MS / 1e3)}s). Raise ZELARI_PROVIDER_STREAM_MAX_MS if needed.`
29158
29165
  );
29159
29166
  }
29160
- const waitMs = Math.min(opts.idleMs, remaining);
29167
+ const idleElapsed = now - opts.lastUsefulAt();
29168
+ if (idleElapsed >= opts.idleMs) {
29169
+ throw new Error(
29170
+ `Provider stream idle for ${Math.round(idleElapsed / 1e3)}s (no content tokens \u2014 keep-alive frames don't count). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS.`
29171
+ );
29172
+ }
29173
+ const waitMs = Math.min(opts.idleMs - idleElapsed, remaining);
29161
29174
  let idleTimer;
29162
29175
  let onAbort;
29163
29176
  try {
@@ -29444,6 +29457,7 @@ function openaiCompatibleProvider(config2) {
29444
29457
  if (args === null) continue;
29445
29458
  toolCallAccumulator.delete(idx);
29446
29459
  emittedToolCall = true;
29460
+ markUseful();
29447
29461
  yield {
29448
29462
  kind: "tool_call",
29449
29463
  toolCallId: existing.id || `tc-${idx}`,
@@ -29454,6 +29468,10 @@ function openaiCompatibleProvider(config2) {
29454
29468
  toolCallAccumulator.clear();
29455
29469
  };
29456
29470
  const streamDeadline = Date.now() + PROVIDER_STREAM_MAX_MS;
29471
+ let lastUsefulAt = Date.now();
29472
+ const markUseful = () => {
29473
+ lastUsefulAt = Date.now();
29474
+ };
29457
29475
  try {
29458
29476
  while (true) {
29459
29477
  let chunk;
@@ -29461,7 +29479,8 @@ function openaiCompatibleProvider(config2) {
29461
29479
  chunk = await readChunkWithTimeout(reader, {
29462
29480
  idleMs: PROVIDER_STREAM_IDLE_MS,
29463
29481
  deadlineMs: streamDeadline,
29464
- signal: params.signal
29482
+ signal: params.signal,
29483
+ lastUsefulAt: () => lastUsefulAt
29465
29484
  });
29466
29485
  } catch (err) {
29467
29486
  const msg = err instanceof Error ? err.message : String(err);
@@ -29502,6 +29521,7 @@ function openaiCompatibleProvider(config2) {
29502
29521
  const completionTokens = typeof parsed.usage.completion_tokens === "number" ? parsed.usage.completion_tokens : 0;
29503
29522
  const totalTokens = typeof parsed.usage.total_tokens === "number" ? parsed.usage.total_tokens : promptTokens + completionTokens;
29504
29523
  const cachedPromptTokens = parseCachedPromptTokens(parsed.usage);
29524
+ markUseful();
29505
29525
  yield {
29506
29526
  kind: "usage",
29507
29527
  usage: {
@@ -29513,10 +29533,12 @@ function openaiCompatibleProvider(config2) {
29513
29533
  };
29514
29534
  }
29515
29535
  if (typeof delta?.content === "string" && delta.content.length > 0) {
29536
+ markUseful();
29516
29537
  yield { kind: "text", delta: delta.content };
29517
29538
  }
29518
29539
  const reasoning = delta?.reasoning_content ?? delta?.reasoning;
29519
29540
  if (typeof reasoning === "string" && reasoning.length > 0) {
29541
+ markUseful();
29520
29542
  yield { kind: "thinking", delta: reasoning };
29521
29543
  }
29522
29544
  const details = delta?.reasoning_details;
@@ -29528,9 +29550,13 @@ function openaiCompatibleProvider(config2) {
29528
29550
  if (t.startsWith(reasoningDetailsBuf)) {
29529
29551
  const piece = t.slice(reasoningDetailsBuf.length);
29530
29552
  reasoningDetailsBuf = t;
29531
- if (piece.length > 0) yield { kind: "thinking", delta: piece };
29553
+ if (piece.length > 0) {
29554
+ markUseful();
29555
+ yield { kind: "thinking", delta: piece };
29556
+ }
29532
29557
  } else {
29533
29558
  reasoningDetailsBuf += t;
29559
+ markUseful();
29534
29560
  yield { kind: "thinking", delta: t };
29535
29561
  }
29536
29562
  }
@@ -29551,6 +29577,7 @@ function openaiCompatibleProvider(config2) {
29551
29577
  }
29552
29578
  if (choice?.finish_reason) {
29553
29579
  yield* flushToolAccumulator();
29580
+ markUseful();
29554
29581
  const reason = choice.finish_reason === "stop" && emittedToolCall ? "tool_calls" : choice.finish_reason;
29555
29582
  yield { kind: "finish", reason };
29556
29583
  }
@@ -48395,9 +48422,17 @@ function WorkingIndicator({
48395
48422
  // src/cli/components/LiveRegion.tsx
48396
48423
  var LIVE_STREAM_TAIL_LINES = 10;
48397
48424
  var MAX_LIVE_TOOLS = 4;
48398
- function LiveRegion({ live, busy, elapsedMs = null }) {
48425
+ function LiveRegion({
48426
+ live,
48427
+ busy,
48428
+ elapsedMs = null,
48429
+ waitingForUser = false,
48430
+ waitingSinceMs = 0
48431
+ }) {
48399
48432
  const { streaming, runningTools } = live;
48400
- if (!streaming && runningTools.length === 0 && !busy) return null;
48433
+ if (!streaming && runningTools.length === 0 && !busy && !waitingForUser) {
48434
+ return null;
48435
+ }
48401
48436
  const visibleTools = runningTools.slice(0, MAX_LIVE_TOOLS);
48402
48437
  const hiddenTools = runningTools.length - visibleTools.length;
48403
48438
  return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", paddingX: 1 }, streaming && streaming.role === "assistant" && /* @__PURE__ */ React5.createElement(
@@ -48409,7 +48444,7 @@ function LiveRegion({ live, busy, elapsedMs = null }) {
48409
48444
  memberName: streaming.memberName,
48410
48445
  memberId: streaming.memberId
48411
48446
  }
48412
- ), visibleTools.map((t) => /* @__PURE__ */ React5.createElement(Box4, { key: t.id, flexDirection: "column" }, renderMessage(t, true))), hiddenTools > 0 ? /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2026 +", hiddenTools, " more tools") : null, busy && runningTools.length === 0 && !streaming && /* @__PURE__ */ React5.createElement(WorkingIndicator, { elapsedMs }));
48447
+ ), visibleTools.map((t) => /* @__PURE__ */ React5.createElement(Box4, { key: t.id, flexDirection: "column" }, renderMessage(t, true))), hiddenTools > 0 ? /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2026 +", hiddenTools, " more tools") : null, waitingForUser && /* @__PURE__ */ React5.createElement(Text5, { color: "yellow", bold: true }, "\u23F8 In attesa della TUA risposta", waitingSinceMs > 0 ? ` (${Math.floor(Math.max(0, Date.now() - waitingSinceMs) / 1e3)}s)` : "", " \u2014 \u2191/\u2193 + Invio \xB7 Esc = annulla"), busy && runningTools.length === 0 && !streaming && !waitingForUser && /* @__PURE__ */ React5.createElement(WorkingIndicator, { elapsedMs }));
48413
48448
  }
48414
48449
  function StreamingTail(m) {
48415
48450
  const lines = m.content.split("\n");
@@ -51259,6 +51294,22 @@ init_taskTool();
51259
51294
 
51260
51295
  // src/cli/hooks/permissionPicker.ts
51261
51296
  init_toolPermissions();
51297
+
51298
+ // src/cli/hooks/askUserTimeout.ts
51299
+ function askUserTimeoutMs() {
51300
+ const raw = process.env.ZELARI_ASK_USER_TIMEOUT_MS?.trim();
51301
+ if (!raw) return 3e5;
51302
+ const n = Number.parseInt(raw, 10);
51303
+ if (!Number.isFinite(n) || n < 0) return 3e5;
51304
+ return n;
51305
+ }
51306
+ function armPickerTimeout(onFire, ms) {
51307
+ if (ms <= 0) return () => void 0;
51308
+ const id = setTimeout(onFire, ms);
51309
+ return () => clearTimeout(id);
51310
+ }
51311
+
51312
+ // src/cli/hooks/permissionPicker.ts
51262
51313
  function createPermissionAskHandler(opts) {
51263
51314
  const { setPicker: setPicker2, appendSystem: appendSystem2 } = opts;
51264
51315
  return (req) => new Promise((resolve3) => {
@@ -51273,13 +51324,25 @@ ${detail}
51273
51324
  Date.now()
51274
51325
  );
51275
51326
  let settled = false;
51327
+ const askTimeoutMs = askUserTimeoutMs();
51328
+ let cancelAskTimeout = () => void 0;
51276
51329
  const finish = (ok, note) => {
51277
51330
  if (settled) return;
51278
51331
  settled = true;
51332
+ cancelAskTimeout();
51279
51333
  setPicker2(null);
51280
51334
  if (note) appendSystem2?.(note, Date.now());
51281
51335
  resolve3(ok);
51282
51336
  };
51337
+ cancelAskTimeout = armPickerTimeout(
51338
+ () => finish(
51339
+ false,
51340
+ `[permission] ask for "${req.toolName}" timed out after ${Math.round(
51341
+ askTimeoutMs / 1e3
51342
+ )}s \u2014 denied (tool can be re-run). Knob: ZELARI_ASK_USER_TIMEOUT_MS.`
51343
+ ),
51344
+ askTimeoutMs
51345
+ );
51283
51346
  const items = [
51284
51347
  { value: "allow", label: "Allow once" },
51285
51348
  {
@@ -51629,12 +51692,28 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
51629
51692
  Date.now()
51630
51693
  );
51631
51694
  let settled = false;
51695
+ const askTimeoutMs = askUserTimeoutMs();
51696
+ let cancelAskTimeout = () => void 0;
51632
51697
  const finish = (value) => {
51633
51698
  if (settled) return;
51634
51699
  settled = true;
51700
+ cancelAskTimeout();
51635
51701
  setPicker2(null);
51636
51702
  resolve3(value);
51637
51703
  };
51704
+ cancelAskTimeout = armPickerTimeout(
51705
+ () => {
51706
+ appendSystem(
51707
+ setMessages,
51708
+ `[ask_user] nessuna risposta entro ${Math.round(
51709
+ askTimeoutMs / 1e3
51710
+ )}s \u2014 proseguo con assunzione documentata (ZELARI_ASK_USER_TIMEOUT_MS).`,
51711
+ Date.now()
51712
+ );
51713
+ finish(null);
51714
+ },
51715
+ askTimeoutMs
51716
+ );
51638
51717
  setPicker2({
51639
51718
  kind: "clarification",
51640
51719
  title: req.question,
@@ -52344,12 +52423,28 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
52344
52423
  Date.now()
52345
52424
  );
52346
52425
  let settled = false;
52426
+ const askTimeoutMs = askUserTimeoutMs();
52427
+ let cancelAskTimeout = () => void 0;
52347
52428
  const finish = (value) => {
52348
52429
  if (settled) return;
52349
52430
  settled = true;
52431
+ cancelAskTimeout();
52350
52432
  setPicker2(null);
52351
52433
  resolve3(value);
52352
52434
  };
52435
+ cancelAskTimeout = armPickerTimeout(
52436
+ () => {
52437
+ appendSystem(
52438
+ setMessages,
52439
+ `[council ask_user] nessuna risposta entro ${Math.round(
52440
+ askTimeoutMs / 1e3
52441
+ )}s \u2014 il membro prosegue con assunzione documentata (ZELARI_ASK_USER_TIMEOUT_MS).`,
52442
+ Date.now()
52443
+ );
52444
+ finish(null);
52445
+ },
52446
+ askTimeoutMs
52447
+ );
52353
52448
  setPicker2({
52354
52449
  kind: "clarification",
52355
52450
  title: req.question,
@@ -53114,12 +53209,25 @@ function usePermissionBroker(opts) {
53114
53209
  return;
53115
53210
  }
53116
53211
  let settled = false;
53212
+ let cancelWaitTimeout;
53117
53213
  const finish = (value) => {
53118
53214
  if (settled) return;
53119
53215
  settled = true;
53216
+ cancelWaitTimeout?.();
53120
53217
  setPicker2(null);
53121
53218
  resolve3(value);
53122
53219
  };
53220
+ const waitTimeoutMs = askUserTimeoutMs();
53221
+ cancelWaitTimeout = armPickerTimeout(() => {
53222
+ appendSystem(
53223
+ setMessages,
53224
+ `[external agent] nessuna risposta entro ${Math.round(
53225
+ waitTimeoutMs / 1e3
53226
+ )}s \u2014 richiesta annullata.`,
53227
+ Date.now()
53228
+ );
53229
+ finish(null);
53230
+ }, waitTimeoutMs);
53123
53231
  appendSystem(
53124
53232
  setMessages,
53125
53233
  `[external agent] ${req.question}
@@ -56395,6 +56503,11 @@ function App() {
56395
56503
  const [mode, setMode] = useState8("kraken");
56396
56504
  const [phase2, setPhaseUi] = useState8("build");
56397
56505
  const [picker, setPicker2] = useState8(null);
56506
+ const [pickerSince, setPickerSince] = useState8(0);
56507
+ useEffect8(() => {
56508
+ if (picker) setPickerSince((s) => s > 0 ? s : Date.now());
56509
+ else setPickerSince(0);
56510
+ }, [picker]);
56398
56511
  const activeProviderSpec = getActiveProvider();
56399
56512
  const activeModel = providerConfig.modelByProvider[activeProviderSpec.id];
56400
56513
  const session = useSession();
@@ -56519,7 +56632,16 @@ function App() {
56519
56632
  columns: size.columns || 80,
56520
56633
  rows: size.rows || 24
56521
56634
  }
56522
- ) : renderMessage(item)), /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "row" }, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", flexGrow: 1, paddingX: 1 }, /* @__PURE__ */ React10.createElement(LiveRegion, { live: session.live, busy, elapsedMs: timer.elapsedMs }), picker ? /* @__PURE__ */ React10.createElement(
56635
+ ) : renderMessage(item)), /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "row" }, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", flexGrow: 1, paddingX: 1 }, /* @__PURE__ */ React10.createElement(
56636
+ LiveRegion,
56637
+ {
56638
+ live: session.live,
56639
+ busy,
56640
+ elapsedMs: timer.elapsedMs,
56641
+ waitingForUser: picker !== null,
56642
+ waitingSinceMs: pickerSince
56643
+ }
56644
+ ), picker ? /* @__PURE__ */ React10.createElement(
56523
56645
  SelectList,
56524
56646
  {
56525
56647
  title: picker.title,