@letta-ai/letta-code 0.14.7 → 0.14.8

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 (2) hide show
  1. package/letta.js +1085 -640
  2. package/package.json +2 -2
package/letta.js CHANGED
@@ -166,20 +166,39 @@ var init_error = __esm(() => {
166
166
  });
167
167
 
168
168
  // src/utils/debug.ts
169
+ import { appendFileSync } from "node:fs";
170
+ import { format } from "node:util";
169
171
  function isDebugEnabled() {
170
172
  const debug = process.env.LETTA_DEBUG;
171
173
  return debug === "1" || debug === "true";
172
174
  }
175
+ function getDebugFile() {
176
+ const path = process.env.LETTA_DEBUG_FILE;
177
+ return path && path.trim().length > 0 ? path : null;
178
+ }
179
+ function writeDebugLine(prefix, message, args) {
180
+ const debugFile = getDebugFile();
181
+ const line = `${format(`[${prefix}] ${message}`, ...args)}
182
+ `;
183
+ if (debugFile) {
184
+ try {
185
+ appendFileSync(debugFile, line, { encoding: "utf8" });
186
+ return;
187
+ } catch {}
188
+ }
189
+ console.log(line.trimEnd());
190
+ }
173
191
  function debugLog(prefix, message, ...args) {
174
192
  if (isDebugEnabled()) {
175
- console.log(`[${prefix}] ${message}`, ...args);
193
+ writeDebugLine(prefix, message, args);
176
194
  }
177
195
  }
178
196
  function debugWarn(prefix, message, ...args) {
179
197
  if (isDebugEnabled()) {
180
- console.warn(`[${prefix}] ${message}`, ...args);
198
+ writeDebugLine(prefix, `WARN: ${message}`, args);
181
199
  }
182
200
  }
201
+ var init_debug = () => {};
183
202
 
184
203
  // node_modules/@letta-ai/letta-client/internal/tslib.mjs
185
204
  function __classPrivateFieldSet(receiver, state, value, kind, f) {
@@ -493,7 +512,7 @@ function maybe_map(val, fn) {
493
512
  }
494
513
  return fn(val);
495
514
  }
496
- var has = (obj, key) => (has = Object.hasOwn ?? Function.prototype.call.bind(Object.prototype.hasOwnProperty), has(obj, key)), hex_table, limit = 1024, encode = (str, _defaultEncoder, charset, _kind, format) => {
515
+ var has = (obj, key) => (has = Object.hasOwn ?? Function.prototype.call.bind(Object.prototype.hasOwnProperty), has(obj, key)), hex_table, limit = 1024, encode = (str, _defaultEncoder, charset, _kind, format2) => {
497
516
  if (str.length === 0) {
498
517
  return str;
499
518
  }
@@ -514,7 +533,7 @@ var has = (obj, key) => (has = Object.hasOwn ?? Function.prototype.call.bind(Obj
514
533
  const arr = [];
515
534
  for (let i = 0;i < segment.length; ++i) {
516
535
  let c = segment.charCodeAt(i);
517
- if (c === 45 || c === 46 || c === 95 || c === 126 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || format === RFC1738 && (c === 40 || c === 41)) {
536
+ if (c === 45 || c === 46 || c === 95 || c === 126 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || format2 === RFC1738 && (c === 40 || c === 41)) {
518
537
  arr[arr.length] = segment.charAt(i);
519
538
  continue;
520
539
  }
@@ -554,7 +573,7 @@ var init_utils = __esm(() => {
554
573
  function is_non_nullish_primitive(v) {
555
574
  return typeof v === "string" || typeof v === "number" || typeof v === "boolean" || typeof v === "symbol" || typeof v === "bigint";
556
575
  }
557
- function inner_stringify(object, prefix, generateArrayPrefix, commaRoundTrip, allowEmptyArrays, strictNullHandling, skipNulls, encodeDotInKeys, encoder, filter, sort, allowDots, serializeDate, format, formatter, encodeValuesOnly, charset, sideChannel) {
576
+ function inner_stringify(object, prefix, generateArrayPrefix, commaRoundTrip, allowEmptyArrays, strictNullHandling, skipNulls, encodeDotInKeys, encoder, filter, sort, allowDots, serializeDate, format2, formatter, encodeValuesOnly, charset, sideChannel) {
558
577
  let obj = object;
559
578
  let tmp_sc = sideChannel;
560
579
  let step = 0;
@@ -587,15 +606,15 @@ function inner_stringify(object, prefix, generateArrayPrefix, commaRoundTrip, al
587
606
  }
588
607
  if (obj === null) {
589
608
  if (strictNullHandling) {
590
- return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, "key", format) : prefix;
609
+ return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, "key", format2) : prefix;
591
610
  }
592
611
  obj = "";
593
612
  }
594
613
  if (is_non_nullish_primitive(obj) || is_buffer(obj)) {
595
614
  if (encoder) {
596
- const key_value = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, "key", format);
615
+ const key_value = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, "key", format2);
597
616
  return [
598
- formatter?.(key_value) + "=" + formatter?.(encoder(obj, defaults.encoder, charset, "value", format))
617
+ formatter?.(key_value) + "=" + formatter?.(encoder(obj, defaults.encoder, charset, "value", format2))
599
618
  ];
600
619
  }
601
620
  return [formatter?.(prefix) + "=" + formatter?.(String(obj))];
@@ -632,7 +651,7 @@ function inner_stringify(object, prefix, generateArrayPrefix, commaRoundTrip, al
632
651
  sideChannel.set(object, step);
633
652
  const valueSideChannel = new WeakMap;
634
653
  valueSideChannel.set(sentinel, sideChannel);
635
- push_to_array(values, inner_stringify(value, key_prefix, generateArrayPrefix, commaRoundTrip, allowEmptyArrays, strictNullHandling, skipNulls, encodeDotInKeys, generateArrayPrefix === "comma" && encodeValuesOnly && isArray(obj) ? null : encoder, filter, sort, allowDots, serializeDate, format, formatter, encodeValuesOnly, charset, valueSideChannel));
654
+ push_to_array(values, inner_stringify(value, key_prefix, generateArrayPrefix, commaRoundTrip, allowEmptyArrays, strictNullHandling, skipNulls, encodeDotInKeys, generateArrayPrefix === "comma" && encodeValuesOnly && isArray(obj) ? null : encoder, filter, sort, allowDots, serializeDate, format2, formatter, encodeValuesOnly, charset, valueSideChannel));
636
655
  }
637
656
  return values;
638
657
  }
@@ -650,14 +669,14 @@ function normalize_stringify_options(opts = defaults) {
650
669
  if (typeof opts.charset !== "undefined" && opts.charset !== "utf-8" && opts.charset !== "iso-8859-1") {
651
670
  throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");
652
671
  }
653
- let format = default_format;
672
+ let format2 = default_format;
654
673
  if (typeof opts.format !== "undefined") {
655
674
  if (!has(formatters, opts.format)) {
656
675
  throw new TypeError("Unknown format option provided.");
657
676
  }
658
- format = opts.format;
677
+ format2 = opts.format;
659
678
  }
660
- const formatter = formatters[format];
679
+ const formatter = formatters[format2];
661
680
  let filter = defaults.filter;
662
681
  if (typeof opts.filter === "function" || isArray(opts.filter)) {
663
682
  filter = opts.filter;
@@ -688,7 +707,7 @@ function normalize_stringify_options(opts = defaults) {
688
707
  encoder: typeof opts.encoder === "function" ? opts.encoder : defaults.encoder,
689
708
  encodeValuesOnly: typeof opts.encodeValuesOnly === "boolean" ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
690
709
  filter,
691
- format,
710
+ format: format2,
692
711
  formatter,
693
712
  serializeDate: typeof opts.serializeDate === "function" ? opts.serializeDate : defaults.serializeDate,
694
713
  skipNulls: typeof opts.skipNulls === "boolean" ? opts.skipNulls : defaults.skipNulls,
@@ -3108,7 +3127,7 @@ var package_default;
3108
3127
  var init_package = __esm(() => {
3109
3128
  package_default = {
3110
3129
  name: "@letta-ai/letta-code",
3111
- version: "0.14.7",
3130
+ version: "0.14.8",
3112
3131
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3113
3132
  type: "module",
3114
3133
  bin: {
@@ -3170,7 +3189,7 @@ var init_package = __esm(() => {
3170
3189
  check: "bun run scripts/check.js",
3171
3190
  dev: "bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
3172
3191
  build: "node scripts/postinstall-patches.js && bun run build.js",
3173
- prepare: "bun run build",
3192
+ prepublishOnly: "bun run build",
3174
3193
  postinstall: "node scripts/postinstall-patches.js"
3175
3194
  },
3176
3195
  "lint-staged": {
@@ -4287,6 +4306,7 @@ class SettingsManager {
4287
4306
  }
4288
4307
  var DEFAULT_SETTINGS, DEFAULT_PROJECT_SETTINGS, DEFAULT_LOCAL_PROJECT_SETTINGS, DEFAULT_LETTA_API_URL = "https://api.letta.com", settingsManager;
4289
4308
  var init_settings_manager = __esm(async () => {
4309
+ init_debug();
4290
4310
  init_fs();
4291
4311
  await init_secrets();
4292
4312
  DEFAULT_SETTINGS = {
@@ -7367,38 +7387,38 @@ var require_react_development = __commonJS((exports, module) => {
7367
7387
  ReactSharedInternals.ReactDebugCurrentFrame = ReactDebugCurrentFrame;
7368
7388
  ReactSharedInternals.ReactCurrentActQueue = ReactCurrentActQueue;
7369
7389
  }
7370
- function warn(format) {
7390
+ function warn(format2) {
7371
7391
  {
7372
7392
  {
7373
7393
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1;_key < _len; _key++) {
7374
7394
  args[_key - 1] = arguments[_key];
7375
7395
  }
7376
- printWarning("warn", format, args);
7396
+ printWarning("warn", format2, args);
7377
7397
  }
7378
7398
  }
7379
7399
  }
7380
- function error(format) {
7400
+ function error(format2) {
7381
7401
  {
7382
7402
  {
7383
7403
  for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1;_key2 < _len2; _key2++) {
7384
7404
  args[_key2 - 1] = arguments[_key2];
7385
7405
  }
7386
- printWarning("error", format, args);
7406
+ printWarning("error", format2, args);
7387
7407
  }
7388
7408
  }
7389
7409
  }
7390
- function printWarning(level, format, args) {
7410
+ function printWarning(level, format2, args) {
7391
7411
  {
7392
7412
  var ReactDebugCurrentFrame2 = ReactSharedInternals.ReactDebugCurrentFrame;
7393
7413
  var stack = ReactDebugCurrentFrame2.getStackAddendum();
7394
7414
  if (stack !== "") {
7395
- format += "%s";
7415
+ format2 += "%s";
7396
7416
  args = args.concat([stack]);
7397
7417
  }
7398
7418
  var argsWithFormat = args.map(function(item) {
7399
7419
  return String(item);
7400
7420
  });
7401
- argsWithFormat.unshift("Warning: " + format);
7421
+ argsWithFormat.unshift("Warning: " + format2);
7402
7422
  Function.prototype.apply.call(console[level], console, argsWithFormat);
7403
7423
  }
7404
7424
  }
@@ -11750,38 +11770,38 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
11750
11770
  suppressWarning = newSuppressWarning;
11751
11771
  }
11752
11772
  }
11753
- function warn(format) {
11773
+ function warn(format2) {
11754
11774
  {
11755
11775
  if (!suppressWarning) {
11756
11776
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1;_key < _len; _key++) {
11757
11777
  args[_key - 1] = arguments[_key];
11758
11778
  }
11759
- printWarning("warn", format, args);
11779
+ printWarning("warn", format2, args);
11760
11780
  }
11761
11781
  }
11762
11782
  }
11763
- function error(format) {
11783
+ function error(format2) {
11764
11784
  {
11765
11785
  if (!suppressWarning) {
11766
11786
  for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1;_key2 < _len2; _key2++) {
11767
11787
  args[_key2 - 1] = arguments[_key2];
11768
11788
  }
11769
- printWarning("error", format, args);
11789
+ printWarning("error", format2, args);
11770
11790
  }
11771
11791
  }
11772
11792
  }
11773
- function printWarning(level, format, args) {
11793
+ function printWarning(level, format2, args) {
11774
11794
  {
11775
11795
  var ReactDebugCurrentFrame2 = ReactSharedInternals.ReactDebugCurrentFrame;
11776
11796
  var stack = ReactDebugCurrentFrame2.getStackAddendum();
11777
11797
  if (stack !== "") {
11778
- format += "%s";
11798
+ format2 += "%s";
11779
11799
  args = args.concat([stack]);
11780
11800
  }
11781
11801
  var argsWithFormat = args.map(function(item) {
11782
11802
  return String(item);
11783
11803
  });
11784
- argsWithFormat.unshift("Warning: " + format);
11804
+ argsWithFormat.unshift("Warning: " + format2);
11785
11805
  Function.prototype.apply.call(console[level], console, argsWithFormat);
11786
11806
  }
11787
11807
  }
@@ -31094,28 +31114,28 @@ var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
31094
31114
  return null;
31095
31115
  }
31096
31116
  var ReactSharedInternals = React10.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
31097
- function error(format) {
31117
+ function error(format2) {
31098
31118
  {
31099
31119
  {
31100
31120
  for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1;_key2 < _len2; _key2++) {
31101
31121
  args[_key2 - 1] = arguments[_key2];
31102
31122
  }
31103
- printWarning("error", format, args);
31123
+ printWarning("error", format2, args);
31104
31124
  }
31105
31125
  }
31106
31126
  }
31107
- function printWarning(level, format, args) {
31127
+ function printWarning(level, format2, args) {
31108
31128
  {
31109
31129
  var ReactDebugCurrentFrame2 = ReactSharedInternals.ReactDebugCurrentFrame;
31110
31130
  var stack = ReactDebugCurrentFrame2.getStackAddendum();
31111
31131
  if (stack !== "") {
31112
- format += "%s";
31132
+ format2 += "%s";
31113
31133
  args = args.concat([stack]);
31114
31134
  }
31115
31135
  var argsWithFormat = args.map(function(item) {
31116
31136
  return String(item);
31117
31137
  });
31118
- argsWithFormat.unshift("Warning: " + format);
31138
+ argsWithFormat.unshift("Warning: " + format2);
31119
31139
  Function.prototype.apply.call(console[level], console, argsWithFormat);
31120
31140
  }
31121
31141
  }
@@ -33252,10 +33272,11 @@ function getSnapshot() {
33252
33272
  return tick;
33253
33273
  }
33254
33274
  function AnimatedLogo({
33255
- color = colors.welcome.accent
33275
+ color = colors.welcome.accent,
33276
+ animate = true
33256
33277
  }) {
33257
33278
  const tick2 = import_react24.useSyncExternalStore(subscribe, getSnapshot);
33258
- const frame = tick2 % logoFrames.length;
33279
+ const frame = animate ? tick2 % logoFrames.length : 0;
33259
33280
  const logoLines = logoFrames[frame]?.split(`
33260
33281
  `) ?? [];
33261
33282
  return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(jsx_dev_runtime5.Fragment, {
@@ -33409,7 +33430,8 @@ function WelcomeScreen({
33409
33430
  paddingLeft: 1,
33410
33431
  paddingRight: 2,
33411
33432
  children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(AnimatedLogo, {
33412
- color: colors.welcome.accent
33433
+ color: colors.welcome.accent,
33434
+ animate: loadingState !== "ready"
33413
33435
  }, undefined, false, undefined, this)
33414
33436
  }, undefined, false, undefined, this),
33415
33437
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
@@ -34598,13 +34620,19 @@ function windowsLaunchers(command) {
34598
34620
  return [];
34599
34621
  const launchers = [];
34600
34622
  const seen = new Set;
34623
+ const powerShellCommand = trimmed.startsWith("&") || trimmed.startsWith('"') || trimmed.startsWith("'") ? trimmed.startsWith("&") ? trimmed : `& ${trimmed}` : trimmed;
34601
34624
  pushUnique(launchers, seen, [
34602
34625
  "powershell.exe",
34603
34626
  "-NoProfile",
34604
34627
  "-Command",
34605
- trimmed
34628
+ powerShellCommand
34629
+ ]);
34630
+ pushUnique(launchers, seen, [
34631
+ "pwsh",
34632
+ "-NoProfile",
34633
+ "-Command",
34634
+ powerShellCommand
34606
34635
  ]);
34607
- pushUnique(launchers, seen, ["pwsh", "-NoProfile", "-Command", trimmed]);
34608
34636
  const envComSpecRaw = process.env.ComSpec || process.env.COMSPEC;
34609
34637
  const envComSpec = envComSpecRaw?.trim();
34610
34638
  if (envComSpec) {
@@ -34667,6 +34695,7 @@ var init_types = __esm(() => {
34667
34695
  TOOL_EVENTS = new Set([
34668
34696
  "PreToolUse",
34669
34697
  "PostToolUse",
34698
+ "PostToolUseFailure",
34670
34699
  "PermissionRequest"
34671
34700
  ]);
34672
34701
  });
@@ -34737,8 +34766,10 @@ function executeWithLauncher(launcher, inputJson, workingDirectory, input, timeo
34737
34766
  const safeResolve = (result) => {
34738
34767
  if (!resolved) {
34739
34768
  resolved = true;
34740
- const exitLabel = result.exitCode === 0 /* ALLOW */ ? "\x1B[32m✓ allowed\x1B[0m" : result.exitCode === 2 /* BLOCK */ ? "\x1B[31m✗ blocked\x1B[0m" : "\x1B[33m⚠ error\x1B[0m";
34741
- console.log(`\x1B[90m[hook] ${command}\x1B[0m`);
34769
+ const exitCode = result.exitCode === 0 /* ALLOW */ ? 0 : result.exitCode === 2 /* BLOCK */ ? 2 : 1;
34770
+ const exitColor = result.exitCode === 0 /* ALLOW */ ? "\x1B[32m" : result.exitCode === 2 /* BLOCK */ ? "\x1B[31m" : "\x1B[33m";
34771
+ const exitLabel = result.timedOut ? `${exitColor}timeout\x1B[0m` : `${exitColor}exit ${exitCode}\x1B[0m`;
34772
+ console.log(`\x1B[90m[hook:${input.event_type}] ${command}\x1B[0m`);
34742
34773
  console.log(`\x1B[90m ⎿ ${exitLabel} (${result.durationMs}ms)\x1B[0m`);
34743
34774
  if (result.stdout) {
34744
34775
  console.log(`\x1B[90m ⎿ (stdout)\x1B[0m`);
@@ -34908,7 +34939,8 @@ var init_executor = __esm(() => {
34908
34939
  function loadGlobalHooks() {
34909
34940
  try {
34910
34941
  return settingsManager.getSettings().hooks || {};
34911
- } catch {
34942
+ } catch (error) {
34943
+ debugLog("hooks", "loadGlobalHooks: Settings not initialized yet", error);
34912
34944
  return {};
34913
34945
  }
34914
34946
  }
@@ -34920,7 +34952,8 @@ async function loadProjectHooks(workingDirectory = process.cwd()) {
34920
34952
  await settingsManager.loadProjectSettings(workingDirectory);
34921
34953
  }
34922
34954
  return settingsManager.getProjectSettings(workingDirectory)?.hooks || {};
34923
- } catch {
34955
+ } catch (error) {
34956
+ debugLog("hooks", "loadProjectHooks: Settings not available", error);
34924
34957
  return {};
34925
34958
  }
34926
34959
  }
@@ -34932,7 +34965,8 @@ async function loadProjectLocalHooks(workingDirectory = process.cwd()) {
34932
34965
  await settingsManager.loadLocalProjectSettings(workingDirectory);
34933
34966
  }
34934
34967
  return settingsManager.getLocalProjectSettings(workingDirectory)?.hooks || {};
34935
- } catch {
34968
+ } catch (error) {
34969
+ debugLog("hooks", "loadProjectLocalHooks: Settings not available", error);
34936
34970
  return {};
34937
34971
  }
34938
34972
  }
@@ -34983,7 +35017,8 @@ function matchesTool(pattern, toolName) {
34983
35017
  try {
34984
35018
  const regex2 = new RegExp(`^(?:${pattern})$`);
34985
35019
  return regex2.test(toolName);
34986
- } catch {
35020
+ } catch (error) {
35021
+ debugLog("hooks", `matchesTool: Invalid regex pattern "${pattern}", falling back to exact match`, error);
34987
35022
  return pattern === toolName;
34988
35023
  }
34989
35024
  }
@@ -35026,15 +35061,20 @@ function areHooksDisabled(workingDirectory = process.cwd()) {
35026
35061
  if (projectDisabled === true) {
35027
35062
  return true;
35028
35063
  }
35029
- } catch {}
35064
+ } catch (error) {
35065
+ debugLog("hooks", "areHooksDisabled: Project settings not loaded, skipping", error);
35066
+ }
35030
35067
  try {
35031
35068
  const localDisabled = settingsManager.getLocalProjectSettings(workingDirectory)?.hooks?.disabled;
35032
35069
  if (localDisabled === true) {
35033
35070
  return true;
35034
35071
  }
35035
- } catch {}
35072
+ } catch (error) {
35073
+ debugLog("hooks", "areHooksDisabled: Local project settings not loaded, skipping", error);
35074
+ }
35036
35075
  return false;
35037
- } catch {
35076
+ } catch (error) {
35077
+ debugLog("hooks", "areHooksDisabled: Failed to check hooks disabled status", error);
35038
35078
  return false;
35039
35079
  }
35040
35080
  }
@@ -35046,6 +35086,7 @@ async function getHooksForEvent(event, toolName, workingDirectory = process.cwd(
35046
35086
  return getMatchingHooks(config, event, toolName);
35047
35087
  }
35048
35088
  var init_loader = __esm(async () => {
35089
+ init_debug();
35049
35090
  init_types();
35050
35091
  await init_settings_manager();
35051
35092
  });
@@ -35084,6 +35125,31 @@ async function runPostToolUseHooks(toolName, toolInput, toolResult, toolCallId,
35084
35125
  };
35085
35126
  return executeHooksParallel(hooks, input, workingDirectory);
35086
35127
  }
35128
+ async function runPostToolUseFailureHooks(toolName, toolInput, errorMessage, errorType, toolCallId, workingDirectory = process.cwd(), agentId, precedingReasoning, precedingAssistantMessage) {
35129
+ const hooks = await getHooksForEvent("PostToolUseFailure", toolName, workingDirectory);
35130
+ if (hooks.length === 0) {
35131
+ return { blocked: false, errored: false, feedback: [], results: [] };
35132
+ }
35133
+ const input = {
35134
+ event_type: "PostToolUseFailure",
35135
+ working_directory: workingDirectory,
35136
+ tool_name: toolName,
35137
+ tool_input: toolInput,
35138
+ tool_call_id: toolCallId,
35139
+ error_message: errorMessage,
35140
+ error_type: errorType,
35141
+ agent_id: agentId,
35142
+ preceding_reasoning: precedingReasoning,
35143
+ preceding_assistant_message: precedingAssistantMessage
35144
+ };
35145
+ const result = await executeHooksParallel(hooks, input, workingDirectory);
35146
+ return {
35147
+ blocked: false,
35148
+ errored: result.errored,
35149
+ feedback: result.feedback,
35150
+ results: result.results
35151
+ };
35152
+ }
35087
35153
  async function runPermissionRequestHooks(toolName, toolInput, permissionType, scope, workingDirectory = process.cwd()) {
35088
35154
  const hooks = await getHooksForEvent("PermissionRequest", toolName, workingDirectory);
35089
35155
  if (hooks.length === 0) {
@@ -35194,7 +35260,19 @@ async function runSessionStartHooks(isNewSession, agentId, agentName, conversati
35194
35260
  agent_name: agentName,
35195
35261
  conversation_id: conversationId
35196
35262
  };
35197
- return executeHooks(hooks, input, workingDirectory);
35263
+ const result = await executeHooks(hooks, input, workingDirectory);
35264
+ const feedback = [];
35265
+ for (const hookResult of result.results) {
35266
+ if (hookResult.stdout?.trim()) {
35267
+ feedback.push(hookResult.stdout.trim());
35268
+ }
35269
+ }
35270
+ return {
35271
+ blocked: false,
35272
+ errored: result.errored,
35273
+ feedback,
35274
+ results: result.results
35275
+ };
35198
35276
  }
35199
35277
  async function runSessionEndHooks(durationMs, messageCount, toolCallCount, agentId, conversationId, workingDirectory = process.cwd()) {
35200
35278
  const hooks = await getHooksForEvent("SessionEnd", undefined, workingDirectory);
@@ -36939,8 +37017,8 @@ function getShellEnv() {
36939
37017
  const env3 = { ...process.env };
36940
37018
  const rgBinDir = getRipgrepBinDir();
36941
37019
  if (rgBinDir) {
36942
- const currentPath = env3.PATH || "";
36943
- env3.PATH = `${rgBinDir}${path3.delimiter}${currentPath}`;
37020
+ const pathKey = Object.keys(env3).find((k) => k.toUpperCase() === "PATH") || "PATH";
37021
+ env3[pathKey] = `${rgBinDir}${path3.delimiter}${env3[pathKey] || ""}`;
36944
37022
  }
36945
37023
  try {
36946
37024
  env3.LETTA_AGENT_ID = getCurrentAgentId();
@@ -39701,11 +39779,11 @@ var require_picomatch = __commonJS((exports, module) => {
39701
39779
  return { isMatch: false, output: "" };
39702
39780
  }
39703
39781
  const opts = options || {};
39704
- const format = opts.format || (posix ? utils.toPosixSlashes : null);
39782
+ const format2 = opts.format || (posix ? utils.toPosixSlashes : null);
39705
39783
  let match = input === glob2;
39706
- let output = match && format ? format(input) : input;
39784
+ let output = match && format2 ? format2(input) : input;
39707
39785
  if (match === false) {
39708
- output = format ? format(input) : input;
39786
+ output = format2 ? format2(input) : input;
39709
39787
  match = output === glob2;
39710
39788
  }
39711
39789
  if (match === false || opts.capture === true) {
@@ -39979,14 +40057,14 @@ async function getImageDimensions(buffer) {
39979
40057
  const output = execSync(`magick identify -format "%w %h %m" "${tempInput}"`, {
39980
40058
  encoding: "utf-8"
39981
40059
  });
39982
- const [width, height, format] = output.trim().split(" ");
39983
- if (!width || !height || !format) {
40060
+ const [width, height, format2] = output.trim().split(" ");
40061
+ if (!width || !height || !format2) {
39984
40062
  throw new Error("Failed to get image dimensions");
39985
40063
  }
39986
40064
  return {
39987
40065
  width: parseInt(width, 10),
39988
40066
  height: parseInt(height, 10),
39989
- format: format.toLowerCase()
40067
+ format: format2.toLowerCase()
39990
40068
  };
39991
40069
  } finally {
39992
40070
  unlinkSync2(tempInput);
@@ -40055,9 +40133,9 @@ async function compressToFitByteLimit(buffer, currentWidth, currentHeight) {
40055
40133
  }
40056
40134
  }
40057
40135
  async function resizeImageIfNeeded(buffer, inputMediaType) {
40058
- const { width, height, format } = await getImageDimensions(buffer);
40136
+ const { width, height, format: format2 } = await getImageDimensions(buffer);
40059
40137
  const needsResize = width > MAX_IMAGE_WIDTH || height > MAX_IMAGE_HEIGHT;
40060
- const isPassthroughFormat = format === "png" || format === "jpeg" || format === "jpg";
40138
+ const isPassthroughFormat = format2 === "png" || format2 === "jpeg" || format2 === "jpg";
40061
40139
  if (!needsResize && isPassthroughFormat) {
40062
40140
  const compressed = await compressToFitByteLimit(buffer, width, height);
40063
40141
  if (compressed) {
@@ -40078,7 +40156,7 @@ async function resizeImageIfNeeded(buffer, inputMediaType) {
40078
40156
  const tempOutput2 = join12(tmpdir(), `resize-output-${Date.now()}-${Math.random().toString(36).slice(2)}`);
40079
40157
  let outputBuffer2;
40080
40158
  let outputMediaType;
40081
- if (format === "jpeg" || format === "jpg") {
40159
+ if (format2 === "jpeg" || format2 === "jpg") {
40082
40160
  execSync(`magick "${tempInput}" -resize ${MAX_IMAGE_WIDTH}x${MAX_IMAGE_HEIGHT}> -quality 85 "${tempOutput2}.jpg"`, {
40083
40161
  stdio: "ignore"
40084
40162
  });
@@ -45680,10 +45758,10 @@ var require_output = __commonJS((exports, module) => {
45680
45758
  }
45681
45759
  return this;
45682
45760
  }
45683
- function toFormat(format, options) {
45684
- const actualFormat = formats.get((is.object(format) && is.string(format.id) ? format.id : format).toLowerCase());
45761
+ function toFormat(format2, options) {
45762
+ const actualFormat = formats.get((is.object(format2) && is.string(format2.id) ? format2.id : format2).toLowerCase());
45685
45763
  if (!actualFormat) {
45686
- throw is.invalidParameterError("format", `one of: ${[...formats.keys()].join(", ")}`, format);
45764
+ throw is.invalidParameterError("format", `one of: ${[...formats.keys()].join(", ")}`, format2);
45687
45765
  }
45688
45766
  return this[actualFormat](options);
45689
45767
  }
@@ -46405,11 +46483,11 @@ var require_utility = __commonJS((exports, module) => {
46405
46483
  var sharp = require_sharp();
46406
46484
  var runtimePlatform = runtimePlatformArch();
46407
46485
  var libvipsVersion = sharp.libvipsVersion();
46408
- var format = sharp.format();
46409
- format.heif.output.alias = ["avif", "heic"];
46410
- format.jpeg.output.alias = ["jpe", "jpg"];
46411
- format.tiff.output.alias = ["tif"];
46412
- format.jp2k.output.alias = ["j2c", "j2k", "jp2", "jpx"];
46486
+ var format2 = sharp.format();
46487
+ format2.heif.output.alias = ["avif", "heic"];
46488
+ format2.jpeg.output.alias = ["jpe", "jpg"];
46489
+ format2.tiff.output.alias = ["tif"];
46490
+ format2.jp2k.output.alias = ["j2c", "j2k", "jp2", "jpx"];
46413
46491
  var interpolators = {
46414
46492
  nearest: "nearest",
46415
46493
  bilinear: "bilinear",
@@ -46437,9 +46515,9 @@ var require_utility = __commonJS((exports, module) => {
46437
46515
  }
46438
46516
  }
46439
46517
  versions.sharp = require_package().version;
46440
- if (versions.heif && format.heif) {
46441
- format.heif.input.fileSuffix = [".avif"];
46442
- format.heif.output.alias = ["avif"];
46518
+ if (versions.heif && format2.heif) {
46519
+ format2.heif.input.fileSuffix = [".avif"];
46520
+ format2.heif.output.alias = ["avif"];
46443
46521
  }
46444
46522
  function cache4(options) {
46445
46523
  if (is.bool(options)) {
@@ -46497,7 +46575,7 @@ var require_utility = __commonJS((exports, module) => {
46497
46575
  Sharp.concurrency = concurrency;
46498
46576
  Sharp.counters = counters;
46499
46577
  Sharp.simd = simd;
46500
- Sharp.format = format;
46578
+ Sharp.format = format2;
46501
46579
  Sharp.interpolators = interpolators;
46502
46580
  Sharp.versions = versions;
46503
46581
  Sharp.queue = queue;
@@ -46576,9 +46654,9 @@ async function resizeImageIfNeeded2(buffer, inputMediaType) {
46576
46654
  const metadata = await image2.metadata();
46577
46655
  const width = metadata.width ?? 0;
46578
46656
  const height = metadata.height ?? 0;
46579
- const format = metadata.format;
46657
+ const format2 = metadata.format;
46580
46658
  const needsResize = width > MAX_IMAGE_WIDTH2 || height > MAX_IMAGE_HEIGHT2;
46581
- const isPassthroughFormat = format === "png" || format === "jpeg";
46659
+ const isPassthroughFormat = format2 === "png" || format2 === "jpeg";
46582
46660
  if (!needsResize && isPassthroughFormat) {
46583
46661
  const compressed2 = await compressToFitByteLimit2(buffer, width, height);
46584
46662
  if (compressed2) {
@@ -46599,7 +46677,7 @@ async function resizeImageIfNeeded2(buffer, inputMediaType) {
46599
46677
  });
46600
46678
  let outputBuffer2;
46601
46679
  let outputMediaType;
46602
- if (format === "jpeg") {
46680
+ if (format2 === "jpeg") {
46603
46681
  outputBuffer2 = await resized.jpeg({ quality: 85 }).toBuffer();
46604
46682
  outputMediaType = "image/jpeg";
46605
46683
  } else {
@@ -54363,7 +54441,10 @@ async function getAvailableModelHandles(options) {
54363
54441
  function prefetchAvailableModelHandles() {
54364
54442
  getAvailableModelHandles().catch(() => {});
54365
54443
  }
54366
- function getModelContextWindow(handle) {
54444
+ async function getModelContextWindow(handle) {
54445
+ if (!cache4) {
54446
+ await getAvailableModelHandles();
54447
+ }
54367
54448
  return cache4?.contextWindows.get(handle);
54368
54449
  }
54369
54450
  var CACHE_TTL_MS, cache4 = null, inflight = null;
@@ -57621,19 +57702,22 @@ var init_esm7 = __esm(() => {
57621
57702
 
57622
57703
  // src/permissions/matcher.ts
57623
57704
  import { resolve as resolve13 } from "node:path";
57705
+ function normalizePath(p) {
57706
+ return p.replace(/\\/g, "/");
57707
+ }
57624
57708
  function matchesFilePattern(query, pattern, workingDirectory) {
57625
57709
  const queryMatch = query.match(/^([^(]+)\((.+)\)$/);
57626
57710
  if (!queryMatch || !queryMatch[1] || !queryMatch[2]) {
57627
57711
  return false;
57628
57712
  }
57629
57713
  const queryTool = queryMatch[1];
57630
- const filePath = queryMatch[2];
57714
+ const filePath = normalizePath(queryMatch[2]);
57631
57715
  const patternMatch = pattern.match(/^([^(]+)\((.+)\)$/);
57632
57716
  if (!patternMatch || !patternMatch[1] || !patternMatch[2]) {
57633
57717
  return false;
57634
57718
  }
57635
57719
  const patternTool = patternMatch[1];
57636
- let globPattern = patternMatch[2];
57720
+ let globPattern = normalizePath(patternMatch[2]);
57637
57721
  if (queryTool !== patternTool) {
57638
57722
  return false;
57639
57723
  }
@@ -57647,11 +57731,12 @@ function matchesFilePattern(query, pattern, workingDirectory) {
57647
57731
  if (globPattern.startsWith("//")) {
57648
57732
  globPattern = globPattern.slice(1);
57649
57733
  }
57650
- const absoluteFilePath = resolve13(workingDirectory, filePath);
57734
+ const absoluteFilePath = normalizePath(resolve13(workingDirectory, filePath));
57651
57735
  if (globPattern.startsWith("/")) {
57652
57736
  return minimatch2(absoluteFilePath, globPattern);
57653
57737
  }
57654
- const relativeFilePath = filePath.startsWith("/") ? absoluteFilePath.replace(`${workingDirectory}/`, "") : filePath;
57738
+ const normalizedWorkingDir = normalizePath(workingDirectory);
57739
+ const relativeFilePath = filePath.startsWith("/") ? absoluteFilePath.replace(`${normalizedWorkingDir}/`, "") : filePath;
57655
57740
  return minimatch2(relativeFilePath, globPattern) || minimatch2(absoluteFilePath, globPattern);
57656
57741
  }
57657
57742
  function extractActualCommand(command) {
@@ -58971,10 +59056,51 @@ async function executeTool(name, args, options) {
58971
59056
  const responseSize = typeof flattenedResponse === "string" ? flattenedResponse.length : JSON.stringify(flattenedResponse).length;
58972
59057
  telemetry2.trackToolUsage(internalName, toolStatus === "success", duration, responseSize, toolStatus === "error" ? "tool_error" : undefined, stderr ? stderr.join(`
58973
59058
  `) : undefined);
58974
- runPostToolUseHooks(internalName, args, {
58975
- status: toolStatus,
58976
- output: getDisplayableToolReturn(flattenedResponse)
58977
- }, options?.toolCallId, undefined, undefined, undefined, undefined).catch(() => {});
59059
+ let postToolUseFeedback = [];
59060
+ try {
59061
+ const postHookResult = await runPostToolUseHooks(internalName, args, {
59062
+ status: toolStatus,
59063
+ output: getDisplayableToolReturn(flattenedResponse)
59064
+ }, options?.toolCallId, undefined, undefined, undefined, undefined);
59065
+ postToolUseFeedback = postHookResult.feedback;
59066
+ } catch (error) {
59067
+ debugLog("hooks", "PostToolUse hook error (success path)", error);
59068
+ }
59069
+ let postToolUseFailureFeedback = [];
59070
+ if (toolStatus === "error") {
59071
+ const errorOutput = typeof flattenedResponse === "string" ? flattenedResponse : JSON.stringify(flattenedResponse);
59072
+ try {
59073
+ const failureHookResult = await runPostToolUseFailureHooks(internalName, args, errorOutput, "tool_error", options?.toolCallId, undefined, undefined, undefined, undefined);
59074
+ postToolUseFailureFeedback = failureHookResult.feedback;
59075
+ } catch (error) {
59076
+ debugLog("hooks", "PostToolUseFailure hook error (tool returned error)", error);
59077
+ }
59078
+ }
59079
+ const allFeedback = [...postToolUseFeedback, ...postToolUseFailureFeedback];
59080
+ if (allFeedback.length > 0) {
59081
+ const feedbackMessage = `
59082
+
59083
+ [Hook feedback]:
59084
+ ${allFeedback.join(`
59085
+ `)}`;
59086
+ let finalToolReturn;
59087
+ if (typeof flattenedResponse === "string") {
59088
+ finalToolReturn = flattenedResponse + feedbackMessage;
59089
+ } else if (Array.isArray(flattenedResponse)) {
59090
+ finalToolReturn = [
59091
+ ...flattenedResponse,
59092
+ { type: "text", text: feedbackMessage }
59093
+ ];
59094
+ } else {
59095
+ finalToolReturn = flattenedResponse;
59096
+ }
59097
+ return {
59098
+ toolReturn: finalToolReturn,
59099
+ status: toolStatus,
59100
+ ...stdout && { stdout },
59101
+ ...stderr && { stderr }
59102
+ };
59103
+ }
58978
59104
  return {
58979
59105
  toolReturn: flattenedResponse,
58980
59106
  status: toolStatus,
@@ -58987,9 +59113,28 @@ async function executeTool(name, args, options) {
58987
59113
  const errorType = isAbort ? "abort" : error instanceof Error ? error.name : "unknown";
58988
59114
  const errorMessage = isAbort ? INTERRUPTED_BY_USER : error instanceof Error ? error.message : String(error);
58989
59115
  telemetry2.trackToolUsage(internalName, false, duration, errorMessage.length, errorType, errorMessage);
58990
- runPostToolUseHooks(internalName, args, { status: "error", output: errorMessage }, options?.toolCallId, undefined, undefined, undefined, undefined).catch(() => {});
59116
+ let postToolUseFeedback = [];
59117
+ try {
59118
+ const postHookResult = await runPostToolUseHooks(internalName, args, { status: "error", output: errorMessage }, options?.toolCallId, undefined, undefined, undefined, undefined);
59119
+ postToolUseFeedback = postHookResult.feedback;
59120
+ } catch (error2) {
59121
+ debugLog("hooks", "PostToolUse hook error (error path)", error2);
59122
+ }
59123
+ let postToolUseFailureFeedback = [];
59124
+ try {
59125
+ const failureHookResult = await runPostToolUseFailureHooks(internalName, args, errorMessage, errorType, options?.toolCallId, undefined, undefined, undefined, undefined);
59126
+ postToolUseFailureFeedback = failureHookResult.feedback;
59127
+ } catch (error2) {
59128
+ debugLog("hooks", "PostToolUseFailure hook error (exception path)", error2);
59129
+ }
59130
+ const allFeedback = [...postToolUseFeedback, ...postToolUseFailureFeedback];
59131
+ const finalErrorMessage = allFeedback.length > 0 ? `${errorMessage}
59132
+
59133
+ [Hook feedback]:
59134
+ ${allFeedback.join(`
59135
+ `)}` : errorMessage;
58991
59136
  return {
58992
- toolReturn: errorMessage,
59137
+ toolReturn: finalErrorMessage,
58993
59138
  status: "error"
58994
59139
  };
58995
59140
  }
@@ -59025,6 +59170,7 @@ var init_manager3 = __esm(async () => {
59025
59170
  init_model();
59026
59171
  init_subagents();
59027
59172
  init_constants();
59173
+ init_debug();
59028
59174
  await __promiseAll([
59029
59175
  init_approval_execution(),
59030
59176
  init_hooks(),
@@ -61856,7 +62002,7 @@ function buildModelSettings(modelHandle, updateArgs) {
61856
62002
  async function updateAgentLLMConfig(agentId, modelHandle, updateArgs) {
61857
62003
  const client = await getClient2();
61858
62004
  const modelSettings = buildModelSettings(modelHandle, updateArgs);
61859
- const contextWindow = updateArgs?.context_window;
62005
+ const contextWindow = updateArgs?.context_window ?? await getModelContextWindow(modelHandle);
61860
62006
  const hasModelSettings = Object.keys(modelSettings).length > 0;
61861
62007
  await client.agents.update(agentId, {
61862
62008
  model: modelHandle,
@@ -61938,6 +62084,7 @@ async function updateAgentSystemPromptMemfs(agentId, enableMemfs) {
61938
62084
  var init_modify = __esm(async () => {
61939
62085
  await __promiseAll([
61940
62086
  init_openai_codex_provider(),
62087
+ init_available_models(),
61941
62088
  init_client2()
61942
62089
  ]);
61943
62090
  });
@@ -62109,7 +62256,7 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
62109
62256
  blockProvenance.push({ label: blockId, source: "shared" });
62110
62257
  }
62111
62258
  const modelUpdateArgs = getModelUpdateArgs(modelHandle);
62112
- const contextWindow = modelUpdateArgs?.context_window;
62259
+ const contextWindow = modelUpdateArgs?.context_window ?? await getModelContextWindow(modelHandle);
62113
62260
  let systemPromptContent;
62114
62261
  if (options.systemPromptCustom) {
62115
62262
  systemPromptContent = options.systemPromptCustom;
@@ -62182,6 +62329,7 @@ var init_create = __esm(async () => {
62182
62329
  init_promptAssets();
62183
62330
  init_skills2();
62184
62331
  await __promiseAll([
62332
+ init_available_models(),
62185
62333
  init_client2(),
62186
62334
  init_modify()
62187
62335
  ]);
@@ -62853,16 +63001,6 @@ function isGlobTool(name) {
62853
63001
  }
62854
63002
 
62855
63003
  // src/cli/helpers/accumulator.ts
62856
- var exports_accumulator = {};
62857
- __export(exports_accumulator, {
62858
- toLines: () => toLines,
62859
- setToolCallsRunning: () => setToolCallsRunning,
62860
- onChunk: () => onChunk,
62861
- markIncompleteToolsAsCancelled: () => markIncompleteToolsAsCancelled,
62862
- markCurrentLineAsFinished: () => markCurrentLineAsFinished,
62863
- createBuffers: () => createBuffers,
62864
- appendStreamingOutput: () => appendStreamingOutput
62865
- });
62866
63004
  function appendStreamingOutput(state, chunk, startTime, isStderr = false) {
62867
63005
  const current = state || {
62868
63006
  tailLines: [],
@@ -62963,7 +63101,7 @@ function markCurrentLineAsFinished(b) {
62963
63101
  if (!b.lastOtid) {
62964
63102
  return;
62965
63103
  }
62966
- const prev = b.byId.get(b.lastOtid) || b.byId.get(`${b.lastOtid}-tool`);
63104
+ const prev = b.byId.get(b.lastOtid);
62967
63105
  if (prev && (prev.kind === "assistant" || prev.kind === "reasoning")) {
62968
63106
  markAsFinished(b, b.lastOtid);
62969
63107
  } else {}
@@ -63109,48 +63247,33 @@ function onChunk(b, chunk) {
63109
63247
  }
63110
63248
  case "tool_call_message":
63111
63249
  case "approval_request_message": {
63112
- let id = chunk.otid;
63250
+ handleOtidTransition(b, chunk.otid ?? undefined);
63113
63251
  const toolCall = chunk.tool_call || (Array.isArray(chunk.tool_calls) && chunk.tool_calls.length > 0 ? chunk.tool_calls[0] : null);
63114
- const toolCallId = toolCall?.tool_call_id;
63115
- const name = toolCall?.name;
63116
- const argsText = toolCall?.arguments;
63117
- if (toolCallId && b.toolCallIdToLineId.has(toolCallId)) {
63118
- const existingId = b.toolCallIdToLineId.get(toolCallId);
63119
- if (existingId) {
63120
- id = existingId;
63121
- }
63122
- handleOtidTransition(b, chunk.otid ?? undefined);
63123
- } else {
63124
- if (id && b.byId.has(id)) {
63125
- const existing = b.byId.get(id);
63126
- if (existing && existing.kind === "reasoning") {
63127
- markAsFinished(b, id);
63128
- id = `${id}-tool`;
63129
- } else if (existing && existing.kind === "tool_call") {
63130
- if (toolCallId) {
63131
- id = `${id}-${toolCallId.slice(-8)}`;
63132
- } else {
63133
- id = `${id}-${Date.now().toString(36)}`;
63134
- }
63135
- }
63136
- }
63137
- handleOtidTransition(b, id ?? undefined);
63138
- if (!id) {
63139
- break;
63140
- }
63141
- if (toolCallId)
63142
- b.toolCallIdToLineId.set(toolCallId, id);
63143
- }
63144
- if (!id)
63252
+ if (!toolCall || !toolCall.tool_call_id)
63145
63253
  break;
63254
+ const toolCallId = toolCall.tool_call_id;
63255
+ const name = toolCall.name;
63256
+ const argsText = toolCall.arguments;
63257
+ const id = b.toolCallIdToLineId.get(toolCallId) ?? toolCallId;
63258
+ if (!b.toolCallIdToLineId.has(toolCallId)) {
63259
+ b.toolCallIdToLineId.set(toolCallId, id);
63260
+ }
63146
63261
  const desiredPhase = "ready";
63147
- const line = ensure(b, id, () => ({
63262
+ let line = ensure(b, id, () => ({
63148
63263
  kind: "tool_call",
63149
63264
  id,
63150
- toolCallId: toolCallId ?? undefined,
63265
+ toolCallId,
63151
63266
  name: name ?? undefined,
63152
63267
  phase: desiredPhase
63153
63268
  }));
63269
+ if (name && !line.name || line.toolCallId !== toolCallId) {
63270
+ line = {
63271
+ ...line,
63272
+ toolCallId,
63273
+ name: line.name ?? name ?? undefined
63274
+ };
63275
+ b.byId.set(id, line);
63276
+ }
63154
63277
  if (chunk.message_type === "approval_request_message" && line.phase !== "finished") {
63155
63278
  b.byId.set(id, { ...line, phase: "ready" });
63156
63279
  }
@@ -63182,7 +63305,9 @@ function onChunk(b, chunk) {
63182
63305
  parsedArgs = JSON.parse(toolInfo.toolArgs);
63183
63306
  }
63184
63307
  } catch {}
63185
- runPreToolUseHooks(toolInfo.toolName, parsedArgs, toolCallId, undefined, b.agentId).catch(() => {});
63308
+ runPreToolUseHooks(toolInfo.toolName, parsedArgs, toolCallId, undefined, b.agentId).catch((error) => {
63309
+ debugLog("hooks", "PreToolUse hook error (accumulator)", error);
63310
+ });
63186
63311
  }
63187
63312
  }
63188
63313
  break;
@@ -63231,7 +63356,9 @@ function onChunk(b, chunk) {
63231
63356
  runPostToolUseHooks(serverToolInfo.toolName, parsedArgs, {
63232
63357
  status: status === "success" ? "success" : "error",
63233
63358
  output: resultText
63234
- }, toolCallId, undefined, b.agentId, precedingReasoning, precedingAssistantMessage).catch(() => {});
63359
+ }, toolCallId, undefined, b.agentId, precedingReasoning, precedingAssistantMessage).catch((error) => {
63360
+ debugLog("hooks", "PostToolUse hook error (accumulator)", error);
63361
+ });
63235
63362
  b.serverToolCalls.delete(toolCallId);
63236
63363
  }
63237
63364
  }
@@ -63337,6 +63464,7 @@ function setToolCallsRunning(b, toolCallIds) {
63337
63464
  var MAX_TAIL_LINES = 5, MAX_BUFFER_SIZE = 1e5, CANCEL_REASON_TEXT;
63338
63465
  var init_accumulator = __esm(async () => {
63339
63466
  init_constants();
63467
+ init_debug();
63340
63468
  init_backfill();
63341
63469
  await init_hooks();
63342
63470
  CANCEL_REASON_TEXT = {
@@ -63347,6 +63475,99 @@ var init_accumulator = __esm(async () => {
63347
63475
  };
63348
63476
  });
63349
63477
 
63478
+ // src/cli/helpers/safeJsonParse.ts
63479
+ function safeJsonParse(json) {
63480
+ try {
63481
+ const data = JSON.parse(json);
63482
+ return { success: true, data };
63483
+ } catch (error) {
63484
+ return {
63485
+ success: false,
63486
+ error: error instanceof Error ? error.message : String(error)
63487
+ };
63488
+ }
63489
+ }
63490
+ function safeJsonParseOr(json, defaultValue) {
63491
+ const result = safeJsonParse(json);
63492
+ return result.success ? result.data : defaultValue;
63493
+ }
63494
+
63495
+ // src/cli/helpers/approvalClassification.ts
63496
+ async function getMissingRequiredArgs(toolName, parsedArgs) {
63497
+ const schema = getToolSchema(toolName);
63498
+ const required = schema?.input_schema?.required || [];
63499
+ return required.filter((key) => !(key in parsedArgs) || parsedArgs[key] == null);
63500
+ }
63501
+ async function classifyApprovals(approvals, opts = {}) {
63502
+ const needsUserInput = [];
63503
+ const autoAllowed = [];
63504
+ const autoDenied = [];
63505
+ const denyReasonForAsk = opts.denyReasonForAsk ?? "Tool requires approval (headless mode)";
63506
+ const missingNameReason = opts.missingNameReason ?? "Tool call incomplete - missing name";
63507
+ for (const approval of approvals) {
63508
+ const toolName = approval.toolName;
63509
+ if (!toolName) {
63510
+ autoDenied.push({
63511
+ approval,
63512
+ permission: { decision: "deny", reason: missingNameReason },
63513
+ context: null,
63514
+ parsedArgs: {},
63515
+ denyReason: missingNameReason
63516
+ });
63517
+ continue;
63518
+ }
63519
+ const parsedArgs = safeJsonParseOr(approval.toolArgs || "{}", {});
63520
+ const permission = await checkToolPermission(toolName, parsedArgs);
63521
+ const context3 = opts.getContext ? await opts.getContext(toolName, parsedArgs) : null;
63522
+ let decision = permission.decision;
63523
+ if (opts.alwaysRequiresUserInput?.(toolName) && decision === "allow") {
63524
+ decision = "ask";
63525
+ }
63526
+ if (decision === "ask" && opts.treatAskAsDeny) {
63527
+ autoDenied.push({
63528
+ approval,
63529
+ permission,
63530
+ context: context3,
63531
+ parsedArgs,
63532
+ denyReason: denyReasonForAsk
63533
+ });
63534
+ continue;
63535
+ }
63536
+ if (decision === "allow" && opts.requireArgsForAutoApprove) {
63537
+ const missingRequiredArgs = await getMissingRequiredArgs(toolName, parsedArgs);
63538
+ if (missingRequiredArgs.length > 0) {
63539
+ const denyReason = opts.missingArgsReason ? opts.missingArgsReason(missingRequiredArgs) : `Missing required parameter${missingRequiredArgs.length > 1 ? "s" : ""}: ${missingRequiredArgs.join(", ")}`;
63540
+ autoDenied.push({
63541
+ approval,
63542
+ permission,
63543
+ context: context3,
63544
+ parsedArgs,
63545
+ missingRequiredArgs,
63546
+ denyReason
63547
+ });
63548
+ continue;
63549
+ }
63550
+ }
63551
+ const entry = {
63552
+ approval,
63553
+ permission,
63554
+ context: context3,
63555
+ parsedArgs
63556
+ };
63557
+ if (decision === "ask") {
63558
+ needsUserInput.push(entry);
63559
+ } else if (decision === "deny") {
63560
+ autoDenied.push(entry);
63561
+ } else {
63562
+ autoAllowed.push(entry);
63563
+ }
63564
+ }
63565
+ return { needsUserInput, autoAllowed, autoDenied };
63566
+ }
63567
+ var init_approvalClassification = __esm(async () => {
63568
+ await init_manager3();
63569
+ });
63570
+
63350
63571
  // src/cli/helpers/errorContext.ts
63351
63572
  function setErrorContext(context3) {
63352
63573
  currentContext = { ...currentContext, ...context3 };
@@ -63543,23 +63764,6 @@ var init_errorFormatter = __esm(() => {
63543
63764
  init_errorContext();
63544
63765
  });
63545
63766
 
63546
- // src/cli/helpers/safeJsonParse.ts
63547
- function safeJsonParse(json) {
63548
- try {
63549
- const data = JSON.parse(json);
63550
- return { success: true, data };
63551
- } catch (error) {
63552
- return {
63553
- success: false,
63554
- error: error instanceof Error ? error.message : String(error)
63555
- };
63556
- }
63557
- }
63558
- function safeJsonParseOr(json, defaultValue) {
63559
- const result = safeJsonParse(json);
63560
- return result.success ? result.data : defaultValue;
63561
- }
63562
-
63563
63767
  // src/cli/helpers/streamProcessor.ts
63564
63768
  class StreamProcessor {
63565
63769
  pendingApprovals = new Map;
@@ -63567,7 +63771,6 @@ class StreamProcessor {
63567
63771
  lastRunId = null;
63568
63772
  lastSeqId = null;
63569
63773
  stopReason = null;
63570
- lastApprovalId = null;
63571
63774
  processChunk(chunk) {
63572
63775
  let errorInfo;
63573
63776
  let updatedApproval;
@@ -63607,21 +63810,13 @@ class StreamProcessor {
63607
63810
  this.pendingApprovals.delete(chunk.tool_call_id);
63608
63811
  }
63609
63812
  }
63610
- if (chunk.message_type === "approval_request_message") {
63611
- this.lastApprovalId = chunk.id;
63612
- }
63613
63813
  if (chunk.message_type === "approval_request_message") {
63614
63814
  const toolCalls = Array.isArray(chunk.tool_calls) ? chunk.tool_calls : chunk.tool_call ? [chunk.tool_call] : [];
63615
63815
  for (const toolCall of toolCalls) {
63616
- let id = toolCall?.tool_call_id ?? this.lastApprovalId;
63617
- if (!id) {
63618
- if (this.pendingApprovals.size === 1) {
63619
- id = Array.from(this.pendingApprovals.keys())[0] ?? null;
63620
- }
63621
- }
63622
- if (!id)
63816
+ const toolCallId = toolCall?.tool_call_id;
63817
+ if (!toolCallId)
63623
63818
  continue;
63624
- this.lastApprovalId = id;
63819
+ const id = toolCallId;
63625
63820
  const existing = this.pendingApprovals.get(id) || {
63626
63821
  toolCallId: id,
63627
63822
  toolName: "",
@@ -63652,7 +63847,7 @@ class StreamProcessor {
63652
63847
  }
63653
63848
 
63654
63849
  // src/cli/helpers/stream.ts
63655
- async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessage) {
63850
+ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessage, onChunkProcessed) {
63656
63851
  const startTime = performance.now();
63657
63852
  const requestStartTime = stream2[STREAM_REQUEST_START_TIME];
63658
63853
  let hasLoggedTTFT = false;
@@ -63702,17 +63897,44 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
63702
63897
  const ttft = performance.now() - requestStartTime;
63703
63898
  logTiming(`TTFT: ${formatDuration(ttft)} (from POST to first content)`);
63704
63899
  }
63705
- const { shouldOutput } = streamProcessor.processChunk(chunk);
63900
+ const { shouldOutput, errorInfo, updatedApproval } = streamProcessor.processChunk(chunk);
63706
63901
  if (abortSignal?.aborted) {
63707
63902
  stopReason = "cancelled";
63708
63903
  markIncompleteToolsAsCancelled(buffers, true, "user_interrupt");
63709
63904
  queueMicrotask(refresh);
63710
63905
  break;
63711
63906
  }
63712
- if (shouldOutput) {
63907
+ let shouldOutputChunk = shouldOutput;
63908
+ let shouldAccumulate = shouldOutput;
63909
+ if (onChunkProcessed) {
63910
+ const hookResult = await onChunkProcessed({
63911
+ chunk,
63912
+ shouldOutput: shouldOutputChunk,
63913
+ errorInfo,
63914
+ updatedApproval,
63915
+ streamProcessor
63916
+ });
63917
+ if (hookResult?.shouldOutput !== undefined) {
63918
+ shouldOutputChunk = hookResult.shouldOutput;
63919
+ }
63920
+ if (hookResult?.shouldAccumulate !== undefined) {
63921
+ shouldAccumulate = hookResult.shouldAccumulate;
63922
+ } else {
63923
+ shouldAccumulate = shouldOutputChunk;
63924
+ }
63925
+ if (hookResult?.stopReason) {
63926
+ stopReason = hookResult.stopReason;
63927
+ }
63928
+ } else {
63929
+ shouldAccumulate = shouldOutputChunk;
63930
+ }
63931
+ if (shouldAccumulate) {
63713
63932
  onChunk(buffers, chunk);
63714
63933
  queueMicrotask(refresh);
63715
63934
  }
63935
+ if (stopReason) {
63936
+ break;
63937
+ }
63716
63938
  }
63717
63939
  } catch (e) {
63718
63940
  const errorMessage = e instanceof Error ? e.message : String(e);
@@ -63779,9 +64001,9 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
63779
64001
  fallbackError
63780
64002
  };
63781
64003
  }
63782
- async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onFirstMessage) {
64004
+ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onFirstMessage, onChunkProcessed) {
63783
64005
  const overallStartTime = performance.now();
63784
- let result = await drainStream(stream2, buffers, refresh, abortSignal, onFirstMessage);
64006
+ let result = await drainStream(stream2, buffers, refresh, abortSignal, onFirstMessage, onChunkProcessed);
63785
64007
  if (result.stopReason === "error" && result.lastRunId && result.lastSeqId !== null && abortSignal && !abortSignal.aborted) {
63786
64008
  const originalFallbackError = result.fallbackError;
63787
64009
  try {
@@ -63792,7 +64014,7 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
63792
64014
  starting_after: result.lastSeqId,
63793
64015
  batch_size: 1000
63794
64016
  });
63795
- const resumeResult = await drainStream(resumeStream, buffers, refresh, abortSignal);
64017
+ const resumeResult = await drainStream(resumeStream, buffers, refresh, abortSignal, undefined, onChunkProcessed);
63796
64018
  result = resumeResult;
63797
64019
  } catch (_e) {
63798
64020
  result.fallbackError = originalFallbackError;
@@ -63803,6 +64025,7 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
63803
64025
  }
63804
64026
  var init_stream = __esm(async () => {
63805
64027
  init_error();
64028
+ init_debug();
63806
64029
  init_timing();
63807
64030
  await __promiseAll([
63808
64031
  init_client2(),
@@ -64227,6 +64450,7 @@ async function getResumeData2(client, agent, conversationId) {
64227
64450
  var MESSAGE_HISTORY_LIMIT2 = 15;
64228
64451
  var init_check_approval = __esm(() => {
64229
64452
  init_error();
64453
+ init_debug();
64230
64454
  });
64231
64455
 
64232
64456
  // src/headless.ts
@@ -64313,6 +64537,13 @@ async function handleHeadlessCommand(argv, model, skillsDirectory) {
64313
64537
  }
64314
64538
  const inputFormat = values["input-format"];
64315
64539
  const isBidirectionalMode = inputFormat === "stream-json";
64540
+ process.stdout.on("error", (err) => {
64541
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : undefined;
64542
+ if (code === "EPIPE") {
64543
+ process.exit(0);
64544
+ }
64545
+ throw err;
64546
+ });
64316
64547
  let prompt = positionals.slice(2).join(" ");
64317
64548
  if (!prompt && !isBidirectionalMode) {
64318
64549
  if (!process.stdin.isTTY) {
@@ -64617,10 +64848,7 @@ In headless mode, use:
64617
64848
  const noSkillsFlag = values["no-skills"];
64618
64849
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
64619
64850
  if (!noSkillsFlag && !isSubagent) {
64620
- const createdBlocks = await ensureSkillsBlocks(agent.id);
64621
- if (createdBlocks.length > 0) {
64622
- console.log("Created missing skills blocks for agent compatibility");
64623
- }
64851
+ await ensureSkillsBlocks(agent.id);
64624
64852
  }
64625
64853
  if (memfsFlag) {
64626
64854
  settingsManager.setMemfsEnabled(agent.id, true);
@@ -64638,9 +64866,6 @@ In headless mode, use:
64638
64866
  process.exit(1);
64639
64867
  }
64640
64868
  await updateMemoryFilesystemBlock(agent.id);
64641
- if (syncResult.updatedBlocks.length > 0 || syncResult.createdBlocks.length > 0 || syncResult.deletedBlocks.length > 0 || syncResult.updatedFiles.length > 0 || syncResult.createdFiles.length > 0 || syncResult.deletedFiles.length > 0) {
64642
- console.log(formatMemorySyncSummary(syncResult));
64643
- }
64644
64869
  } catch (error) {
64645
64870
  console.error(`Memory filesystem sync failed: ${error instanceof Error ? error.message : String(error)}`);
64646
64871
  process.exit(1);
@@ -64771,39 +64996,28 @@ In headless mode, use:
64771
64996
  const pendingApprovals = resume.pendingApprovals || [];
64772
64997
  if (pendingApprovals.length === 0)
64773
64998
  break;
64774
- const decisions = [];
64775
- for (const currentApproval of pendingApprovals) {
64776
- const { toolName, toolArgs } = currentApproval;
64777
- const parsedArgs = safeJsonParseOr(toolArgs || "{}", {});
64778
- const permission = await checkToolPermission(toolName, parsedArgs);
64779
- if (permission.decision === "deny" || permission.decision === "ask") {
64780
- const denyReason = permission.decision === "ask" ? "Tool requires approval (headless mode)" : `Permission denied: ${permission.matchedRule || permission.reason}`;
64781
- decisions.push({
64782
- type: "deny",
64783
- approval: currentApproval,
64784
- reason: denyReason
64785
- });
64786
- continue;
64787
- }
64788
- const { getToolSchema: getToolSchema2 } = await init_manager3().then(() => exports_manager2);
64789
- const schema = getToolSchema2(toolName);
64790
- const required = schema?.input_schema?.required || [];
64791
- const missing = required.filter((key) => !(key in parsedArgs) || parsedArgs[key] == null);
64792
- if (missing.length > 0) {
64793
- decisions.push({
64794
- type: "deny",
64795
- approval: currentApproval,
64796
- reason: `Missing required parameter${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
64797
- });
64798
- continue;
64799
- }
64800
- decisions.push({
64999
+ const { autoAllowed, autoDenied } = await classifyApprovals(pendingApprovals, {
65000
+ treatAskAsDeny: true,
65001
+ denyReasonForAsk: "Tool requires approval (headless mode)",
65002
+ requireArgsForAutoApprove: true,
65003
+ missingNameReason: "Tool call incomplete - missing name"
65004
+ });
65005
+ const decisions = [
65006
+ ...autoAllowed.map((ac) => ({
64801
65007
  type: "approve",
64802
- approval: currentApproval,
64803
- reason: permission.reason || "Allowed by permission rule",
64804
- matchedRule: permission.matchedRule || "auto-approved"
64805
- });
64806
- }
65008
+ approval: ac.approval,
65009
+ reason: ac.permission.reason || "Allowed by permission rule",
65010
+ matchedRule: "matchedRule" in ac.permission && ac.permission.matchedRule ? ac.permission.matchedRule : "auto-approved"
65011
+ })),
65012
+ ...autoDenied.map((ac) => {
65013
+ const fallback = "matchedRule" in ac.permission && ac.permission.matchedRule ? `Permission denied: ${ac.permission.matchedRule}` : ac.permission.reason ? `Permission denied: ${ac.permission.reason}` : "Permission denied: Unknown reason";
65014
+ return {
65015
+ type: "deny",
65016
+ approval: ac.approval,
65017
+ reason: ac.denyReason ?? fallback
65018
+ };
65019
+ })
65020
+ ];
64807
65021
  const { executeApprovalBatch: executeApprovalBatch2 } = await init_approval_execution().then(() => exports_approval_execution);
64808
65022
  if (outputFormat === "stream-json") {
64809
65023
  for (const decision of decisions) {
@@ -64923,12 +65137,16 @@ ${SYSTEM_REMINDER_CLOSE}
64923
65137
  let approvals = [];
64924
65138
  let apiDurationMs;
64925
65139
  let lastRunId = null;
65140
+ let approvalPendingRecovery = false;
64926
65141
  if (outputFormat === "stream-json") {
64927
- const startTime = performance.now();
64928
65142
  const autoApprovalEmitted = new Set;
64929
- const streamProcessor = new StreamProcessor;
64930
- for await (const chunk of stream2) {
64931
- const { shouldOutput, errorInfo, updatedApproval } = streamProcessor.processChunk(chunk);
65143
+ const streamJsonHook = async ({
65144
+ chunk,
65145
+ shouldOutput,
65146
+ errorInfo,
65147
+ updatedApproval
65148
+ }) => {
65149
+ let shouldOutputChunk = shouldOutput;
64932
65150
  if (errorInfo && shouldOutput) {
64933
65151
  const errorEvent = {
64934
65152
  type: "error",
@@ -64948,52 +65166,45 @@ ${SYSTEM_REMINDER_CLOSE}
64948
65166
  }
64949
65167
  };
64950
65168
  console.log(JSON.stringify(errorEvent));
64951
- const { onChunk: accumulatorOnChunk } = await init_accumulator().then(() => exports_accumulator);
64952
- accumulatorOnChunk(buffers, chunk);
64953
- continue;
65169
+ shouldOutputChunk = false;
64954
65170
  }
64955
65171
  if (isApprovalPendingError(errorInfo?.detail) || isApprovalPendingError(errorInfo?.message)) {
64956
- if (outputFormat === "stream-json") {
64957
- const recoveryMsg = {
64958
- type: "recovery",
64959
- recovery_type: "approval_pending",
64960
- message: "Detected pending approval conflict; auto-denying stale approval and retrying",
64961
- run_id: lastRunId ?? undefined,
65172
+ const recoveryRunId = errorInfo?.run_id;
65173
+ const recoveryMsg = {
65174
+ type: "recovery",
65175
+ recovery_type: "approval_pending",
65176
+ message: "Detected pending approval conflict; auto-denying stale approval and retrying",
65177
+ run_id: recoveryRunId ?? undefined,
65178
+ session_id: sessionId,
65179
+ uuid: `recovery-${recoveryRunId || crypto.randomUUID()}`
65180
+ };
65181
+ console.log(JSON.stringify(recoveryMsg));
65182
+ approvalPendingRecovery = true;
65183
+ return { stopReason: "error", shouldAccumulate: true };
65184
+ }
65185
+ if (updatedApproval && !autoApprovalEmitted.has(updatedApproval.toolCallId)) {
65186
+ const { autoAllowed } = await classifyApprovals([updatedApproval], {
65187
+ requireArgsForAutoApprove: true,
65188
+ missingNameReason: "Tool call incomplete - missing name"
65189
+ });
65190
+ const [approval] = autoAllowed;
65191
+ if (approval) {
65192
+ const permission = approval.permission;
65193
+ shouldOutputChunk = false;
65194
+ const autoApprovalMsg = {
65195
+ type: "auto_approval",
65196
+ tool_call: {
65197
+ name: approval.approval.toolName,
65198
+ tool_call_id: approval.approval.toolCallId,
65199
+ arguments: approval.approval.toolArgs || "{}"
65200
+ },
65201
+ reason: permission.reason || "Allowed by permission rule",
65202
+ matched_rule: "matchedRule" in permission && permission.matchedRule ? permission.matchedRule : "auto-approved",
64962
65203
  session_id: sessionId,
64963
- uuid: `recovery-${lastRunId || crypto.randomUUID()}`
65204
+ uuid: `auto-approval-${approval.approval.toolCallId}`
64964
65205
  };
64965
- console.log(JSON.stringify(recoveryMsg));
64966
- }
64967
- await resolveAllPendingApprovals();
64968
- stopReason = "error";
64969
- break;
64970
- }
64971
- let shouldOutputChunk = shouldOutput;
64972
- if (updatedApproval && !autoApprovalEmitted.has(updatedApproval.toolCallId) && updatedApproval.toolName) {
64973
- const parsedArgs = safeJsonParseOr(updatedApproval.toolArgs || "{}", null);
64974
- const permission = await checkToolPermission(updatedApproval.toolName, parsedArgs || {});
64975
- if (permission.decision === "allow" && parsedArgs) {
64976
- const { getToolSchema: getToolSchema2 } = await init_manager3().then(() => exports_manager2);
64977
- const schema = getToolSchema2(updatedApproval.toolName);
64978
- const required = schema?.input_schema?.required || [];
64979
- const missing = required.filter((key) => !(key in parsedArgs) || parsedArgs[key] == null);
64980
- if (missing.length === 0) {
64981
- shouldOutputChunk = false;
64982
- const autoApprovalMsg = {
64983
- type: "auto_approval",
64984
- tool_call: {
64985
- name: updatedApproval.toolName,
64986
- tool_call_id: updatedApproval.toolCallId,
64987
- arguments: updatedApproval.toolArgs || "{}"
64988
- },
64989
- reason: permission.reason || "Allowed by permission rule",
64990
- matched_rule: permission.matchedRule || "auto-approved",
64991
- session_id: sessionId,
64992
- uuid: `auto-approval-${updatedApproval.toolCallId}`
64993
- };
64994
- console.log(JSON.stringify(autoApprovalMsg));
64995
- autoApprovalEmitted.add(updatedApproval.toolCallId);
64996
- }
65206
+ console.log(JSON.stringify(autoApprovalMsg));
65207
+ autoApprovalEmitted.add(approval.approval.toolCallId);
64997
65208
  }
64998
65209
  }
64999
65210
  if (shouldOutputChunk) {
@@ -65017,17 +65228,15 @@ ${SYSTEM_REMINDER_CLOSE}
65017
65228
  console.log(JSON.stringify(msg));
65018
65229
  }
65019
65230
  }
65020
- const { onChunk: onChunk2 } = await init_accumulator().then(() => exports_accumulator);
65021
- onChunk2(buffers, chunk);
65022
- }
65023
- stopReason = stopReason || streamProcessor.stopReason || "error";
65024
- apiDurationMs = performance.now() - startTime;
65025
- approvals = streamProcessor.getApprovals();
65026
- lastRunId = streamProcessor.lastRunId;
65231
+ return { shouldOutput: shouldOutputChunk, shouldAccumulate: true };
65232
+ };
65233
+ const result = await drainStreamWithResume(stream2, buffers, () => {}, undefined, undefined, streamJsonHook);
65234
+ stopReason = result.stopReason;
65235
+ approvals = result.approvals || [];
65236
+ apiDurationMs = result.apiDurationMs;
65237
+ lastRunId = result.lastRunId || null;
65027
65238
  if (lastRunId)
65028
65239
  lastKnownRunId = lastRunId;
65029
- const { markCurrentLineAsFinished: markCurrentLineAsFinished2 } = await init_accumulator().then(() => exports_accumulator);
65030
- markCurrentLineAsFinished2(buffers);
65031
65240
  } else {
65032
65241
  const result = await drainStreamWithResume(stream2, buffers, () => {});
65033
65242
  stopReason = result.stopReason;
@@ -65038,6 +65247,10 @@ ${SYSTEM_REMINDER_CLOSE}
65038
65247
  lastKnownRunId = lastRunId;
65039
65248
  }
65040
65249
  sessionStats.endTurn(apiDurationMs);
65250
+ if (approvalPendingRecovery) {
65251
+ await resolveAllPendingApprovals();
65252
+ continue;
65253
+ }
65041
65254
  if (stopReason === "end_turn") {
65042
65255
  llmApiErrorRetries = 0;
65043
65256
  conversationBusyRetries = 0;
@@ -65048,45 +65261,26 @@ ${SYSTEM_REMINDER_CLOSE}
65048
65261
  console.error("Unexpected empty approvals array");
65049
65262
  process.exit(1);
65050
65263
  }
65051
- const decisions = [];
65052
- for (const currentApproval of approvals) {
65053
- const { toolName, toolArgs } = currentApproval;
65054
- const parsedArgs = safeJsonParseOr(toolArgs, {});
65055
- const permission = await checkToolPermission(toolName, parsedArgs);
65056
- if (permission.decision === "deny") {
65057
- const denyReason = `Permission denied: ${permission.matchedRule || permission.reason}`;
65058
- decisions.push({
65059
- type: "deny",
65060
- approval: currentApproval,
65061
- reason: denyReason
65062
- });
65063
- continue;
65064
- }
65065
- if (permission.decision === "ask") {
65066
- decisions.push({
65067
- type: "deny",
65068
- approval: currentApproval,
65069
- reason: "Tool requires approval (headless mode)"
65070
- });
65071
- continue;
65072
- }
65073
- const { getToolSchema: getToolSchema2 } = await init_manager3().then(() => exports_manager2);
65074
- const schema = getToolSchema2(toolName);
65075
- const required = schema?.input_schema?.required || [];
65076
- const missing = required.filter((key) => !(key in parsedArgs) || parsedArgs[key] == null);
65077
- if (missing.length > 0) {
65078
- decisions.push({
65079
- type: "deny",
65080
- approval: currentApproval,
65081
- reason: `Missing required parameter${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
65082
- });
65083
- continue;
65084
- }
65085
- decisions.push({
65264
+ const { autoAllowed, autoDenied } = await classifyApprovals(approvals, {
65265
+ treatAskAsDeny: true,
65266
+ denyReasonForAsk: "Tool requires approval (headless mode)",
65267
+ requireArgsForAutoApprove: true,
65268
+ missingNameReason: "Tool call incomplete - missing name"
65269
+ });
65270
+ const decisions = [
65271
+ ...autoAllowed.map((ac) => ({
65086
65272
  type: "approve",
65087
- approval: currentApproval
65088
- });
65089
- }
65273
+ approval: ac.approval
65274
+ })),
65275
+ ...autoDenied.map((ac) => {
65276
+ const fallback = "matchedRule" in ac.permission && ac.permission.matchedRule ? `Permission denied: ${ac.permission.matchedRule}` : ac.permission.reason ? `Permission denied: ${ac.permission.reason}` : "Permission denied: Unknown reason";
65277
+ return {
65278
+ type: "deny",
65279
+ approval: ac.approval,
65280
+ reason: ac.denyReason ?? fallback
65281
+ };
65282
+ })
65283
+ ];
65090
65284
  const { executeApprovalBatch: executeApprovalBatch2 } = await init_approval_execution().then(() => exports_approval_execution);
65091
65285
  const executedResults = await executeApprovalBatch2(decisions);
65092
65286
  currentInput = [
@@ -65516,109 +65710,110 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
65516
65710
  const stream2 = await sendMessageStream(conversationId, currentInput, {
65517
65711
  agentId: agent.id
65518
65712
  });
65519
- const streamProcessor = new StreamProcessor;
65520
- for await (const chunk of stream2) {
65521
- if (currentAbortController?.signal.aborted) {
65522
- break;
65713
+ const streamJsonHook = ({ chunk, shouldOutput }) => {
65714
+ if (!shouldOutput) {
65715
+ return { shouldAccumulate: true };
65523
65716
  }
65524
- const { shouldOutput } = streamProcessor.processChunk(chunk);
65525
- if (shouldOutput) {
65526
- const chunkWithIds = chunk;
65527
- const uuid = chunkWithIds.otid || chunkWithIds.id;
65528
- if (includePartialMessages) {
65529
- const streamEvent = {
65530
- type: "stream_event",
65531
- event: chunk,
65532
- session_id: sessionId,
65533
- uuid: uuid || crypto.randomUUID()
65534
- };
65535
- console.log(JSON.stringify(streamEvent));
65536
- } else {
65537
- const msg = {
65538
- type: "message",
65539
- ...chunk,
65540
- session_id: sessionId,
65541
- uuid: uuid || crypto.randomUUID()
65542
- };
65543
- console.log(JSON.stringify(msg));
65544
- }
65717
+ const chunkWithIds = chunk;
65718
+ const uuid = chunkWithIds.otid || chunkWithIds.id;
65719
+ if (includePartialMessages) {
65720
+ const streamEvent = {
65721
+ type: "stream_event",
65722
+ event: chunk,
65723
+ session_id: sessionId,
65724
+ uuid: uuid || crypto.randomUUID()
65725
+ };
65726
+ console.log(JSON.stringify(streamEvent));
65727
+ } else {
65728
+ const msg = {
65729
+ type: "message",
65730
+ ...chunk,
65731
+ session_id: sessionId,
65732
+ uuid: uuid || crypto.randomUUID()
65733
+ };
65734
+ console.log(JSON.stringify(msg));
65545
65735
  }
65546
- const { onChunk: onChunk2 } = await init_accumulator().then(() => exports_accumulator);
65547
- onChunk2(buffers, chunk);
65548
- }
65549
- const stopReason = streamProcessor.stopReason || "error";
65736
+ return { shouldAccumulate: true };
65737
+ };
65738
+ const result = await drainStreamWithResume(stream2, buffers, () => {}, currentAbortController?.signal, undefined, streamJsonHook);
65739
+ const stopReason = result.stopReason;
65740
+ const approvals = result.approvals || [];
65550
65741
  if (stopReason === "end_turn") {
65551
65742
  break;
65552
65743
  }
65553
- if (currentAbortController?.signal.aborted) {
65744
+ if (currentAbortController?.signal.aborted || stopReason === "cancelled") {
65554
65745
  break;
65555
65746
  }
65556
65747
  if (stopReason === "requires_approval") {
65557
- const approvals = streamProcessor.getApprovals();
65558
65748
  if (approvals.length === 0) {
65559
65749
  break;
65560
65750
  }
65561
- const decisions = [];
65562
- for (const approval of approvals) {
65563
- const parsedArgs = safeJsonParseOr(approval.toolArgs, {});
65564
- const permission = await checkToolPermission(approval.toolName, parsedArgs);
65565
- if (permission.decision === "allow") {
65751
+ const { autoAllowed, autoDenied, needsUserInput } = await classifyApprovals(approvals, {
65752
+ requireArgsForAutoApprove: true,
65753
+ missingNameReason: "Tool call incomplete - missing name"
65754
+ });
65755
+ const decisions = [
65756
+ ...autoAllowed.map((ac) => ({
65757
+ type: "approve",
65758
+ approval: ac.approval,
65759
+ matchedRule: "matchedRule" in ac.permission && ac.permission.matchedRule ? ac.permission.matchedRule : "auto-approved"
65760
+ })),
65761
+ ...autoDenied.map((ac) => {
65762
+ const fallback = "matchedRule" in ac.permission && ac.permission.matchedRule ? `Permission denied: ${ac.permission.matchedRule}` : ac.permission.reason ? `Permission denied: ${ac.permission.reason}` : "Permission denied: Unknown reason";
65763
+ return {
65764
+ type: "deny",
65765
+ approval: ac.approval,
65766
+ reason: ac.denyReason ?? fallback
65767
+ };
65768
+ })
65769
+ ];
65770
+ for (const approvalItem of autoAllowed) {
65771
+ const permission = approvalItem.permission;
65772
+ const autoApprovalMsg = {
65773
+ type: "auto_approval",
65774
+ tool_call: {
65775
+ name: approvalItem.approval.toolName,
65776
+ tool_call_id: approvalItem.approval.toolCallId,
65777
+ arguments: approvalItem.approval.toolArgs
65778
+ },
65779
+ reason: permission.reason || "auto-approved",
65780
+ matched_rule: "matchedRule" in permission && permission.matchedRule ? permission.matchedRule : "auto-approved",
65781
+ session_id: sessionId,
65782
+ uuid: `auto-approval-${approvalItem.approval.toolCallId}`
65783
+ };
65784
+ console.log(JSON.stringify(autoApprovalMsg));
65785
+ }
65786
+ for (const ac of needsUserInput) {
65787
+ const permResponse = await requestPermission(ac.approval.toolCallId, ac.approval.toolName, ac.parsedArgs);
65788
+ if (permResponse.decision === "allow") {
65789
+ const finalApproval = permResponse.updatedInput ? {
65790
+ ...ac.approval,
65791
+ toolArgs: JSON.stringify(permResponse.updatedInput)
65792
+ } : ac.approval;
65566
65793
  decisions.push({
65567
65794
  type: "approve",
65568
- approval,
65569
- matchedRule: permission.matchedRule || "auto-approved"
65795
+ approval: finalApproval,
65796
+ matchedRule: "SDK callback approved"
65570
65797
  });
65571
65798
  const autoApprovalMsg = {
65572
65799
  type: "auto_approval",
65573
65800
  tool_call: {
65574
- name: approval.toolName,
65575
- tool_call_id: approval.toolCallId,
65576
- arguments: approval.toolArgs
65801
+ name: finalApproval.toolName,
65802
+ tool_call_id: finalApproval.toolCallId,
65803
+ arguments: finalApproval.toolArgs
65577
65804
  },
65578
- reason: permission.reason || "auto-approved",
65579
- matched_rule: permission.matchedRule || "auto-approved",
65805
+ reason: permResponse.reason || "SDK callback approved",
65806
+ matched_rule: "canUseTool callback",
65580
65807
  session_id: sessionId,
65581
- uuid: `auto-approval-${approval.toolCallId}`
65808
+ uuid: `auto-approval-${ac.approval.toolCallId}`
65582
65809
  };
65583
65810
  console.log(JSON.stringify(autoApprovalMsg));
65584
- } else if (permission.decision === "deny") {
65811
+ } else {
65585
65812
  decisions.push({
65586
65813
  type: "deny",
65587
- approval,
65588
- reason: `Permission denied: ${permission.matchedRule || permission.reason}`
65814
+ approval: ac.approval,
65815
+ reason: permResponse.reason || "Denied by SDK callback"
65589
65816
  });
65590
- } else {
65591
- const permResponse = await requestPermission(approval.toolCallId, approval.toolName, parsedArgs);
65592
- if (permResponse.decision === "allow") {
65593
- const finalApproval = permResponse.updatedInput ? {
65594
- ...approval,
65595
- toolArgs: JSON.stringify(permResponse.updatedInput)
65596
- } : approval;
65597
- decisions.push({
65598
- type: "approve",
65599
- approval: finalApproval,
65600
- matchedRule: "SDK callback approved"
65601
- });
65602
- const autoApprovalMsg = {
65603
- type: "auto_approval",
65604
- tool_call: {
65605
- name: finalApproval.toolName,
65606
- tool_call_id: finalApproval.toolCallId,
65607
- arguments: finalApproval.toolArgs
65608
- },
65609
- reason: permResponse.reason || "SDK callback approved",
65610
- matched_rule: "canUseTool callback",
65611
- session_id: sessionId,
65612
- uuid: `auto-approval-${approval.toolCallId}`
65613
- };
65614
- console.log(JSON.stringify(autoApprovalMsg));
65615
- } else {
65616
- decisions.push({
65617
- type: "deny",
65618
- approval,
65619
- reason: permResponse.reason || "Denied by SDK callback"
65620
- });
65621
- }
65622
65817
  }
65623
65818
  }
65624
65819
  const { executeApprovalBatch: executeApprovalBatch2 } = await init_approval_execution().then(() => exports_approval_execution);
@@ -65696,9 +65891,9 @@ var init_headless = __esm(async () => {
65696
65891
  init_memoryFilesystem(),
65697
65892
  init_message(),
65698
65893
  init_accumulator(),
65894
+ init_approvalClassification(),
65699
65895
  init_stream(),
65700
- init_settings_manager(),
65701
- init_manager3()
65896
+ init_settings_manager()
65702
65897
  ]);
65703
65898
  });
65704
65899
 
@@ -69710,7 +69905,8 @@ var init_InlineBashApproval = __esm(async () => {
69710
69905
  onCancel,
69711
69906
  isFocused = true,
69712
69907
  approveAlwaysText,
69713
- allowPersistence = true
69908
+ allowPersistence = true,
69909
+ showPreview = true
69714
69910
  }) => {
69715
69911
  const [selectedOption, setSelectedOption] = import_react36.useState(0);
69716
69912
  const {
@@ -69801,12 +69997,13 @@ var init_InlineBashApproval = __esm(async () => {
69801
69997
  ]
69802
69998
  }, undefined, true, undefined, this), [bashInfo.command, bashInfo.description, solidLine]);
69803
69999
  const hintText = isOnCustomOption ? customReason ? "Enter to submit · Esc to clear" : "Type reason · Esc to cancel" : "Enter to select · Esc to cancel";
70000
+ const optionsMarginTop = showPreview ? 1 : 0;
69804
70001
  return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
69805
70002
  flexDirection: "column",
69806
70003
  children: [
69807
- memoizedCommandContent,
70004
+ showPreview && memoizedCommandContent,
69808
70005
  /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
69809
- marginTop: 1,
70006
+ marginTop: optionsMarginTop,
69810
70007
  flexDirection: "column",
69811
70008
  children: [
69812
70009
  /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
@@ -70126,7 +70323,8 @@ var init_InlineFileEditApproval = __esm(async () => {
70126
70323
  onCancel,
70127
70324
  isFocused = true,
70128
70325
  approveAlwaysText,
70129
- allowPersistence = true
70326
+ allowPersistence = true,
70327
+ showPreview = true
70130
70328
  }) => {
70131
70329
  const [selectedOption, setSelectedOption] = import_react38.useState(0);
70132
70330
  const {
@@ -70354,12 +70552,13 @@ var init_InlineFileEditApproval = __esm(async () => {
70354
70552
  diffKind
70355
70553
  ]);
70356
70554
  const hintText = isOnCustomOption ? customReason ? "Enter to submit · Esc to clear" : "Type reason · Esc to cancel" : "Enter to select · Esc to cancel";
70555
+ const optionsMarginTop = showPreview ? 1 : 0;
70357
70556
  return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
70358
70557
  flexDirection: "column",
70359
70558
  children: [
70360
- memoizedDiffContent,
70559
+ showPreview && memoizedDiffContent,
70361
70560
  /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
70362
- marginTop: 1,
70561
+ marginTop: optionsMarginTop,
70363
70562
  flexDirection: "column",
70364
70563
  children: [
70365
70564
  /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
@@ -70499,7 +70698,8 @@ var init_InlineGenericApproval = __esm(async () => {
70499
70698
  onCancel,
70500
70699
  isFocused = true,
70501
70700
  approveAlwaysText,
70502
- allowPersistence = true
70701
+ allowPersistence = true,
70702
+ showPreview = true
70503
70703
  }) => {
70504
70704
  const [selectedOption, setSelectedOption] = import_react39.useState(0);
70505
70705
  const {
@@ -70590,12 +70790,13 @@ var init_InlineGenericApproval = __esm(async () => {
70590
70790
  ]
70591
70791
  }, undefined, true, undefined, this), [toolName, formattedArgs, solidLine]);
70592
70792
  const hintText = isOnCustomOption ? customReason ? "Enter to submit · Esc to clear" : "Type reason · Esc to cancel" : "Enter to select · Esc to cancel";
70793
+ const optionsMarginTop = showPreview ? 1 : 0;
70593
70794
  return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
70594
70795
  flexDirection: "column",
70595
70796
  children: [
70596
- memoizedToolContent,
70797
+ showPreview && memoizedToolContent,
70597
70798
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
70598
- marginTop: 1,
70799
+ marginTop: optionsMarginTop,
70599
70800
  flexDirection: "column",
70600
70801
  children: [
70601
70802
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
@@ -71670,7 +71871,8 @@ var init_ApprovalSwitch = __esm(async () => {
71670
71871
  onEnterPlanModeApprove,
71671
71872
  onEnterPlanModeReject,
71672
71873
  precomputedDiff,
71673
- allDiffs
71874
+ allDiffs,
71875
+ showPreview = true
71674
71876
  }) => {
71675
71877
  const toolName = approval.toolName;
71676
71878
  if (toolName === "ExitPlanMode" && onPlanApprove && onPlanKeepPlanning) {
@@ -71695,7 +71897,8 @@ var init_ApprovalSwitch = __esm(async () => {
71695
71897
  onCancel,
71696
71898
  isFocused,
71697
71899
  approveAlwaysText,
71698
- allowPersistence
71900
+ allowPersistence,
71901
+ showPreview
71699
71902
  }, undefined, false, undefined, this);
71700
71903
  }
71701
71904
  }
@@ -71710,7 +71913,8 @@ var init_ApprovalSwitch = __esm(async () => {
71710
71913
  onCancel,
71711
71914
  isFocused,
71712
71915
  approveAlwaysText,
71713
- allowPersistence
71916
+ allowPersistence,
71917
+ showPreview
71714
71918
  }, undefined, false, undefined, this);
71715
71919
  }
71716
71920
  }
@@ -71723,12 +71927,14 @@ var init_ApprovalSwitch = __esm(async () => {
71723
71927
  }
71724
71928
  if (toolName === "AskUserQuestion" && onQuestionSubmit) {
71725
71929
  const questions = getQuestions(approval);
71726
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(InlineQuestionApproval, {
71727
- questions,
71728
- onSubmit: onQuestionSubmit,
71729
- onCancel,
71730
- isFocused
71731
- }, undefined, false, undefined, this);
71930
+ if (questions.length > 0) {
71931
+ return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(InlineQuestionApproval, {
71932
+ questions,
71933
+ onSubmit: onQuestionSubmit,
71934
+ onCancel,
71935
+ isFocused
71936
+ }, undefined, false, undefined, this);
71937
+ }
71732
71938
  }
71733
71939
  if (isTaskTool(toolName)) {
71734
71940
  const taskInfo = getTaskInfo(approval);
@@ -71754,7 +71960,8 @@ var init_ApprovalSwitch = __esm(async () => {
71754
71960
  onCancel,
71755
71961
  isFocused,
71756
71962
  approveAlwaysText,
71757
- allowPersistence
71963
+ allowPersistence,
71964
+ showPreview
71758
71965
  }, undefined, false, undefined, this);
71759
71966
  });
71760
71967
  ApprovalSwitch.displayName = "ApprovalSwitch";
@@ -71762,9 +71969,9 @@ var init_ApprovalSwitch = __esm(async () => {
71762
71969
 
71763
71970
  // src/cli/components/AssistantMessageRich.tsx
71764
71971
  var import_react44, jsx_dev_runtime23, normalize6 = (s) => s.replace(/\r\n/g, `
71765
- `).replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, `
71972
+ `).replace(/\n{3,}/g, `
71766
71973
 
71767
- `).replace(/^\n+|\n+$/g, ""), AssistantMessage;
71974
+ `).replace(/^\n+/g, ""), AssistantMessage;
71768
71975
  var init_AssistantMessageRich = __esm(async () => {
71769
71976
  init_useTerminalWidth();
71770
71977
  await __promiseAll([
@@ -72900,14 +73107,6 @@ var init_EventMessage = __esm(async () => {
72900
73107
  color: colors.tool.completed,
72901
73108
  children: "●"
72902
73109
  }, undefined, false, undefined, this);
72903
- const formatArgs2 = () => {
72904
- const stats = line.stats;
72905
- if (stats?.messagesCountBefore !== undefined && stats?.messagesCountAfter !== undefined) {
72906
- return `${stats.messagesCountBefore} → ${stats.messagesCountAfter} messages`;
72907
- }
72908
- return "...";
72909
- };
72910
- const argsDisplay = formatArgs2();
72911
73110
  return /* @__PURE__ */ jsx_dev_runtime33.jsxDEV(Box_default, {
72912
73111
  flexDirection: "column",
72913
73112
  children: [
@@ -72924,16 +73123,12 @@ var init_EventMessage = __esm(async () => {
72924
73123
  width: rightWidth,
72925
73124
  children: isRunning ? /* @__PURE__ */ jsx_dev_runtime33.jsxDEV(CompactingAnimation, {}, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime33.jsxDEV(Text2, {
72926
73125
  bold: true,
72927
- children: [
72928
- "Compact(",
72929
- argsDisplay,
72930
- ")"
72931
- ]
72932
- }, undefined, true, undefined, this)
73126
+ children: "Conversation compacted"
73127
+ }, undefined, false, undefined, this)
72933
73128
  }, undefined, false, undefined, this)
72934
73129
  ]
72935
73130
  }, undefined, true, undefined, this),
72936
- !isRunning && line.summary && /* @__PURE__ */ jsx_dev_runtime33.jsxDEV(jsx_dev_runtime33.Fragment, {
73131
+ !isRunning && line.summary && (process.env.LETTA_DEBUG === "1" || process.env.LETTA_DEBUG === "true") && /* @__PURE__ */ jsx_dev_runtime33.jsxDEV(jsx_dev_runtime33.Fragment, {
72937
73132
  children: [
72938
73133
  /* @__PURE__ */ jsx_dev_runtime33.jsxDEV(Box_default, {
72939
73134
  flexDirection: "row",
@@ -74389,10 +74584,10 @@ var init_registry = __esm(() => {
74389
74584
  }
74390
74585
  },
74391
74586
  "/rename": {
74392
- desc: "Rename the current agent (/rename <name>)",
74587
+ desc: "Rename agent or conversation (/rename agent|convo <name>)",
74393
74588
  order: 24,
74394
74589
  handler: () => {
74395
- return "Renaming agent...";
74590
+ return "Renaming...";
74396
74591
  }
74397
74592
  },
74398
74593
  "/description": {
@@ -75052,7 +75247,8 @@ function loadHooksFromLocation(location, workingDirectory = process.cwd()) {
75052
75247
  case "project-local":
75053
75248
  return settingsManager.getLocalProjectSettings(workingDirectory)?.hooks || {};
75054
75249
  }
75055
- } catch {
75250
+ } catch (error) {
75251
+ debugLog("hooks", "loadHooksFromLocation: Settings not loaded yet", error);
75056
75252
  return {};
75057
75253
  }
75058
75254
  }
@@ -75204,7 +75400,8 @@ function countHooksForEvent(event, workingDirectory = process.cwd()) {
75204
75400
  function isUserHooksDisabled() {
75205
75401
  try {
75206
75402
  return settingsManager.getSettings().hooks?.disabled === true;
75207
- } catch {
75403
+ } catch (error) {
75404
+ debugLog("hooks", "isUserHooksDisabled: Failed to check user hooks disabled status", error);
75208
75405
  return false;
75209
75406
  }
75210
75407
  }
@@ -75218,6 +75415,7 @@ function setHooksDisabled(disabled) {
75218
75415
  });
75219
75416
  }
75220
75417
  var init_writer = __esm(async () => {
75418
+ init_debug();
75221
75419
  init_types();
75222
75420
  await init_settings_manager();
75223
75421
  });
@@ -75261,6 +75459,7 @@ var init_HooksManager = __esm(async () => {
75261
75459
  HOOK_EVENTS = [
75262
75460
  { event: "PreToolUse", description: "Before tool execution" },
75263
75461
  { event: "PostToolUse", description: "After tool execution" },
75462
+ { event: "PostToolUseFailure", description: "After tool execution fails" },
75264
75463
  { event: "PermissionRequest", description: "When permission is requested" },
75265
75464
  { event: "UserPromptSubmit", description: "When user submits a prompt" },
75266
75465
  { event: "Notification", description: "When notifications are sent" },
@@ -77738,28 +77937,28 @@ var require_react_jsx_runtime_development = __commonJS((exports) => {
77738
77937
  return null;
77739
77938
  }
77740
77939
  var ReactSharedInternals = React14.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
77741
- function error(format) {
77940
+ function error(format2) {
77742
77941
  {
77743
77942
  {
77744
77943
  for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1;_key2 < _len2; _key2++) {
77745
77944
  args[_key2 - 1] = arguments[_key2];
77746
77945
  }
77747
- printWarning("error", format, args);
77946
+ printWarning("error", format2, args);
77748
77947
  }
77749
77948
  }
77750
77949
  }
77751
- function printWarning(level, format, args) {
77950
+ function printWarning(level, format2, args) {
77752
77951
  {
77753
77952
  var ReactDebugCurrentFrame2 = ReactSharedInternals.ReactDebugCurrentFrame;
77754
77953
  var stack = ReactDebugCurrentFrame2.getStackAddendum();
77755
77954
  if (stack !== "") {
77756
- format += "%s";
77955
+ format2 += "%s";
77757
77956
  args = args.concat([stack]);
77758
77957
  }
77759
77958
  var argsWithFormat = args.map(function(item) {
77760
77959
  return String(item);
77761
77960
  });
77762
- argsWithFormat.unshift("Warning: " + format);
77961
+ argsWithFormat.unshift("Warning: " + format2);
77763
77962
  Function.prototype.apply.call(console[level], console, argsWithFormat);
77764
77963
  }
77765
77964
  }
@@ -85409,9 +85608,9 @@ var init_ProviderSelector = __esm(async () => {
85409
85608
 
85410
85609
  // src/cli/components/ReasoningMessageRich.tsx
85411
85610
  var import_react79, jsx_dev_runtime56, normalize7 = (s) => s.replace(/\r\n/g, `
85412
- `).replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, `
85611
+ `).replace(/\n{3,}/g, `
85413
85612
 
85414
- `).replace(/^\n+|\n+$/g, ""), ReasoningMessage;
85613
+ `).replace(/^\n+/g, ""), ReasoningMessage;
85415
85614
  var init_ReasoningMessageRich = __esm(async () => {
85416
85615
  init_useTerminalWidth();
85417
85616
  await __promiseAll([
@@ -87386,38 +87585,21 @@ var init_ToolCallMessageRich = __esm(async () => {
87386
87585
  }
87387
87586
  }
87388
87587
  const fallback = displayName.length >= rightWidth;
87389
- const getDotElement = () => {
87588
+ const dotColor = (() => {
87390
87589
  switch (line.phase) {
87391
87590
  case "streaming":
87392
- return /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(Text2, {
87393
- color: colors.tool.streaming,
87394
- children: "●"
87395
- }, undefined, false, undefined, this);
87591
+ return colors.tool.streaming;
87396
87592
  case "ready":
87397
- return /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(BlinkDot, {
87398
- color: colors.tool.pending
87399
- }, undefined, false, undefined, this);
87593
+ return colors.tool.pending;
87400
87594
  case "running":
87401
- return /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(BlinkDot, {
87402
- color: colors.tool.running
87403
- }, undefined, false, undefined, this);
87595
+ return colors.tool.running;
87404
87596
  case "finished":
87405
- if (line.resultOk === false) {
87406
- return /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(Text2, {
87407
- color: colors.tool.error,
87408
- children: "●"
87409
- }, undefined, false, undefined, this);
87410
- }
87411
- return /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(Text2, {
87412
- color: colors.tool.completed,
87413
- children: "●"
87414
- }, undefined, false, undefined, this);
87597
+ return line.resultOk === false ? colors.tool.error : colors.tool.completed;
87415
87598
  default:
87416
- return /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(Text2, {
87417
- children: "●"
87418
- }, undefined, false, undefined, this);
87599
+ return;
87419
87600
  }
87420
- };
87601
+ })();
87602
+ const dotShouldAnimate = line.phase === "ready" || line.phase === "running";
87421
87603
  const getResultElement = () => {
87422
87604
  if (!line.resultText)
87423
87605
  return null;
@@ -88003,7 +88185,10 @@ var init_ToolCallMessageRich = __esm(async () => {
88003
88185
  width: 2,
88004
88186
  flexShrink: 0,
88005
88187
  children: [
88006
- getDotElement(),
88188
+ /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(BlinkDot, {
88189
+ color: dotColor,
88190
+ shouldAnimate: dotShouldAnimate
88191
+ }, undefined, false, undefined, this),
88007
88192
  /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(Text2, {}, undefined, false, undefined, this)
88008
88193
  ]
88009
88194
  }, undefined, true, undefined, this),
@@ -90008,12 +90193,48 @@ function isNonStateCommand(msg) {
90008
90193
  }
90009
90194
  return false;
90010
90195
  }
90196
+ function countWrappedLines(text, width) {
90197
+ if (!text)
90198
+ return 0;
90199
+ const wrapWidth = Math.max(1, width);
90200
+ return text.split(/\r?\n/).reduce((sum, line) => {
90201
+ const len = line.length;
90202
+ const wrapped = Math.max(1, Math.ceil(len / wrapWidth));
90203
+ return sum + wrapped;
90204
+ }, 0);
90205
+ }
90206
+ function countWrappedLinesFromList(lines, width) {
90207
+ if (!lines.length)
90208
+ return 0;
90209
+ const wrapWidth = Math.max(1, width);
90210
+ return lines.reduce((sum, line) => {
90211
+ const len = line.length;
90212
+ const wrapped = Math.max(1, Math.ceil(len / wrapWidth));
90213
+ return sum + wrapped;
90214
+ }, 0);
90215
+ }
90216
+ function estimateAdvancedDiffLines(diff2, width) {
90217
+ const wrapWidth = Math.max(1, width);
90218
+ let total = 0;
90219
+ for (const hunk of diff2.hunks) {
90220
+ for (const line of hunk.lines) {
90221
+ const raw = line.raw || "";
90222
+ if (raw.startsWith("\\"))
90223
+ continue;
90224
+ const text = raw.slice(1);
90225
+ total += Math.max(1, Math.ceil(text.length / wrapWidth));
90226
+ }
90227
+ }
90228
+ return total;
90229
+ }
90011
90230
  function uid4(prefix) {
90012
90231
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
90013
90232
  }
90014
90233
  function sendDesktopNotification(message = "Awaiting your input", level = "info") {
90015
90234
  process.stdout.write("\x07");
90016
- runNotificationHooks(message, level).catch(() => {});
90235
+ runNotificationHooks(message, level).catch((error) => {
90236
+ debugLog("hooks", "Notification hook error", error);
90237
+ });
90017
90238
  }
90018
90239
  async function isRetriableError(stopReason, lastRunId) {
90019
90240
  if (stopReason === "llm_api_error")
@@ -90498,18 +90719,23 @@ function App2({
90498
90719
  closeTrajectorySegment,
90499
90720
  syncTrajectoryElapsedBase
90500
90721
  ]);
90722
+ const sessionStartFeedbackRef = import_react91.useRef([]);
90501
90723
  import_react91.useEffect(() => {
90502
- if (agentId && !sessionHooksRanRef.current) {
90724
+ if (agentId && agentId !== "loading" && !sessionHooksRanRef.current) {
90503
90725
  sessionHooksRanRef.current = true;
90504
90726
  const isNewSession = !initialConversationId;
90505
- runSessionStartHooks(isNewSession, agentId, agentName ?? undefined, conversationIdRef.current ?? undefined).catch(() => {});
90727
+ runSessionStartHooks(isNewSession, agentId, agentName ?? undefined, conversationIdRef.current ?? undefined).then((result) => {
90728
+ if (result.feedback.length > 0) {
90729
+ sessionStartFeedbackRef.current = result.feedback;
90730
+ }
90731
+ }).catch(() => {});
90506
90732
  }
90507
90733
  }, [agentId, agentName, initialConversationId]);
90508
- import_react91.useEffect(() => {
90509
- return () => {
90510
- const durationMs = Date.now() - sessionStartTimeRef.current;
90511
- runSessionEndHooks(durationMs, undefined, undefined, agentIdRef.current ?? undefined, conversationIdRef.current ?? undefined).catch(() => {});
90512
- };
90734
+ const runEndHooks = import_react91.useCallback(async () => {
90735
+ const durationMs = Date.now() - sessionStartTimeRef.current;
90736
+ try {
90737
+ await runSessionEndHooks(durationMs, undefined, undefined, agentIdRef.current ?? undefined, conversationIdRef.current ?? undefined);
90738
+ } catch {}
90513
90739
  }, []);
90514
90740
  import_react91.useEffect(() => {
90515
90741
  return () => {
@@ -90652,9 +90878,34 @@ function App2({
90652
90878
  setStaticRenderEpoch((epoch) => epoch + 1);
90653
90879
  lastClearedColumnsRef.current = pendingColumns;
90654
90880
  }, [columns, streaming]);
90655
- const commitEligibleLines = import_react91.useCallback((b) => {
90881
+ const deferredToolCallCommitsRef = import_react91.useRef(new Map);
90882
+ const [deferredCommitAt, setDeferredCommitAt] = import_react91.useState(null);
90883
+ const resetDeferredToolCallCommits = import_react91.useCallback(() => {
90884
+ deferredToolCallCommitsRef.current.clear();
90885
+ setDeferredCommitAt(null);
90886
+ }, []);
90887
+ const commitEligibleLines = import_react91.useCallback((b, opts) => {
90888
+ const deferToolCalls = opts?.deferToolCalls !== false;
90656
90889
  const newlyCommitted = [];
90657
90890
  let firstTaskIndex = -1;
90891
+ const deferredCommits = deferredToolCallCommitsRef.current;
90892
+ const now = Date.now();
90893
+ let blockedByDeferred = false;
90894
+ const shouldSkipCommittedToolCall = (ln) => {
90895
+ if (ln.kind !== "tool_call")
90896
+ return false;
90897
+ if (!ln.toolCallId || !ln.name)
90898
+ return false;
90899
+ if (ln.phase !== "finished" || ln.resultOk === false)
90900
+ return false;
90901
+ if (!eagerCommittedPreviewsRef.current.has(ln.toolCallId))
90902
+ return false;
90903
+ return isFileEditTool(ln.name) || isFileWriteTool(ln.name) || isPatchTool(ln.name);
90904
+ };
90905
+ if (!deferToolCalls && deferredCommits.size > 0) {
90906
+ deferredCommits.clear();
90907
+ setDeferredCommitAt(null);
90908
+ }
90658
90909
  const hasInProgress = hasInProgressTaskToolCalls(b.order, b.byId, emittedIdsRef.current);
90659
90910
  const finishedTaskToolCalls = collectFinishedTaskToolCalls(b.order, b.byId, emittedIdsRef.current, hasInProgress);
90660
90911
  for (const id of b.order) {
@@ -90695,11 +90946,32 @@ function App2({
90695
90946
  continue;
90696
90947
  }
90697
90948
  if ("phase" in ln && ln.phase === "finished") {
90949
+ if (shouldSkipCommittedToolCall(ln)) {
90950
+ deferredCommits.delete(id);
90951
+ emittedIdsRef.current.add(id);
90952
+ continue;
90953
+ }
90954
+ if (deferToolCalls && ln.kind === "tool_call" && (!ln.name || !isTaskTool(ln.name))) {
90955
+ const commitAt = deferredCommits.get(id);
90956
+ if (commitAt === undefined) {
90957
+ const nextCommitAt = now + TOOL_CALL_COMMIT_DEFER_MS;
90958
+ deferredCommits.set(id, nextCommitAt);
90959
+ setDeferredCommitAt(nextCommitAt);
90960
+ blockedByDeferred = true;
90961
+ break;
90962
+ }
90963
+ if (commitAt > now) {
90964
+ setDeferredCommitAt(commitAt);
90965
+ blockedByDeferred = true;
90966
+ break;
90967
+ }
90968
+ deferredCommits.delete(id);
90969
+ }
90698
90970
  emittedIdsRef.current.add(id);
90699
90971
  newlyCommitted.push({ ...ln });
90700
90972
  }
90701
90973
  }
90702
- if (finishedTaskToolCalls.length > 0) {
90974
+ if (!blockedByDeferred && finishedTaskToolCalls.length > 0) {
90703
90975
  for (const tc of finishedTaskToolCalls) {
90704
90976
  emittedIdsRef.current.add(tc.lineId);
90705
90977
  }
@@ -90707,6 +90979,9 @@ function App2({
90707
90979
  newlyCommitted.splice(firstTaskIndex >= 0 ? firstTaskIndex : newlyCommitted.length, 0, groupItem);
90708
90980
  clearSubagentsByIds(groupItem.agents.map((a) => a.id));
90709
90981
  }
90982
+ if (deferredCommits.size === 0) {
90983
+ setDeferredCommitAt(null);
90984
+ }
90710
90985
  if (newlyCommitted.length > 0) {
90711
90986
  setStaticItems((prev) => [...prev, ...newlyCommitted]);
90712
90987
  }
@@ -90723,6 +90998,108 @@ function App2({
90723
90998
  const precomputedDiffsRef = import_react91.useRef(new Map);
90724
90999
  const lastPlanFilePathRef = import_react91.useRef(null);
90725
91000
  const eagerCommittedPreviewsRef = import_react91.useRef(new Set);
91001
+ const estimateApprovalPreviewLines = import_react91.useCallback((approval) => {
91002
+ const toolName = approval.toolName;
91003
+ if (!toolName)
91004
+ return 0;
91005
+ const args = safeJsonParseOr(approval.toolArgs || "{}", {});
91006
+ const wrapWidth = Math.max(MIN_WRAP_WIDTH, columns - TEXT_WRAP_GUTTER);
91007
+ const diffWrapWidth = Math.max(MIN_WRAP_WIDTH, columns - DIFF_WRAP_GUTTER);
91008
+ if (isShellTool(toolName)) {
91009
+ const t = toolName.toLowerCase();
91010
+ let command = "(no command)";
91011
+ let description = "";
91012
+ if (t === "shell") {
91013
+ const cmdVal = args.command;
91014
+ command = Array.isArray(cmdVal) ? cmdVal.join(" ") : typeof cmdVal === "string" ? cmdVal : "(no command)";
91015
+ description = typeof args.justification === "string" ? args.justification : "";
91016
+ } else {
91017
+ command = typeof args.command === "string" ? args.command : "(no command)";
91018
+ description = typeof args.description === "string" ? args.description : typeof args.justification === "string" ? args.justification : "";
91019
+ }
91020
+ let lines2 = 3;
91021
+ lines2 += countWrappedLines(command, wrapWidth);
91022
+ if (description) {
91023
+ lines2 += countWrappedLines(description, wrapWidth);
91024
+ }
91025
+ return lines2;
91026
+ }
91027
+ if (isFileEditTool(toolName) || isFileWriteTool(toolName) || isPatchTool(toolName)) {
91028
+ const headerLines = 4;
91029
+ let diffLines2 = 0;
91030
+ const toolCallId = approval.toolCallId;
91031
+ if (isPatchTool(toolName) && typeof args.input === "string") {
91032
+ const operations = parsePatchOperations(args.input);
91033
+ operations.forEach((op, idx) => {
91034
+ if (idx > 0)
91035
+ diffLines2 += 1;
91036
+ diffLines2 += 1;
91037
+ const diffKey = toolCallId ? `${toolCallId}:${op.path}` : undefined;
91038
+ const opDiff = diffKey && precomputedDiffsRef.current.has(diffKey) ? precomputedDiffsRef.current.get(diffKey) : undefined;
91039
+ if (opDiff) {
91040
+ diffLines2 += estimateAdvancedDiffLines(opDiff, diffWrapWidth);
91041
+ return;
91042
+ }
91043
+ if (op.kind === "add") {
91044
+ diffLines2 += countWrappedLines(op.content, wrapWidth);
91045
+ return;
91046
+ }
91047
+ if (op.kind === "update") {
91048
+ if (op.patchLines?.length) {
91049
+ diffLines2 += countWrappedLinesFromList(op.patchLines, wrapWidth);
91050
+ } else {
91051
+ diffLines2 += countWrappedLines(op.oldString || "", wrapWidth);
91052
+ diffLines2 += countWrappedLines(op.newString || "", wrapWidth);
91053
+ }
91054
+ return;
91055
+ }
91056
+ diffLines2 += 1;
91057
+ });
91058
+ return headerLines + diffLines2;
91059
+ }
91060
+ const diff2 = toolCallId && precomputedDiffsRef.current.has(toolCallId) ? precomputedDiffsRef.current.get(toolCallId) : undefined;
91061
+ if (diff2) {
91062
+ diffLines2 += estimateAdvancedDiffLines(diff2, diffWrapWidth);
91063
+ return headerLines + diffLines2;
91064
+ }
91065
+ if (Array.isArray(args.edits)) {
91066
+ for (const edit2 of args.edits) {
91067
+ if (!edit2 || typeof edit2 !== "object")
91068
+ continue;
91069
+ const oldString2 = typeof edit2.old_string === "string" ? edit2.old_string : "";
91070
+ const newString2 = typeof edit2.new_string === "string" ? edit2.new_string : "";
91071
+ diffLines2 += countWrappedLines(oldString2, wrapWidth);
91072
+ diffLines2 += countWrappedLines(newString2, wrapWidth);
91073
+ }
91074
+ return headerLines + diffLines2;
91075
+ }
91076
+ if (typeof args.content === "string") {
91077
+ diffLines2 += countWrappedLines(args.content, wrapWidth);
91078
+ return headerLines + diffLines2;
91079
+ }
91080
+ const oldString = typeof args.old_string === "string" ? args.old_string : "";
91081
+ const newString = typeof args.new_string === "string" ? args.new_string : "";
91082
+ diffLines2 += countWrappedLines(oldString, wrapWidth);
91083
+ diffLines2 += countWrappedLines(newString, wrapWidth);
91084
+ return headerLines + diffLines2;
91085
+ }
91086
+ return 0;
91087
+ }, [columns]);
91088
+ const shouldEagerCommitApprovalPreview = import_react91.useCallback((approval) => {
91089
+ if (!terminalRows)
91090
+ return false;
91091
+ const previewLines = estimateApprovalPreviewLines(approval);
91092
+ if (previewLines === 0)
91093
+ return false;
91094
+ return previewLines + APPROVAL_OPTIONS_HEIGHT + APPROVAL_PREVIEW_BUFFER >= terminalRows;
91095
+ }, [estimateApprovalPreviewLines, terminalRows]);
91096
+ const currentApprovalShouldCommitPreview = import_react91.useMemo(() => {
91097
+ if (!currentApproval)
91098
+ return false;
91099
+ if (currentApproval.toolName === "ExitPlanMode")
91100
+ return false;
91101
+ return shouldEagerCommitApprovalPreview(currentApproval);
91102
+ }, [currentApproval, shouldEagerCommitApprovalPreview]);
90726
91103
  const refreshDerived = import_react91.useCallback(() => {
90727
91104
  const b = buffersRef.current;
90728
91105
  setTokenCount(b.tokenCount);
@@ -90730,6 +91107,16 @@ function App2({
90730
91107
  setLines(newLines);
90731
91108
  commitEligibleLines(b);
90732
91109
  }, [commitEligibleLines]);
91110
+ import_react91.useEffect(() => {
91111
+ if (deferredCommitAt === null)
91112
+ return;
91113
+ const delay = Math.max(0, deferredCommitAt - Date.now());
91114
+ const timer = setTimeout(() => {
91115
+ setDeferredCommitAt(null);
91116
+ refreshDerived();
91117
+ }, delay);
91118
+ return () => clearTimeout(timer);
91119
+ }, [deferredCommitAt, refreshDerived]);
90733
91120
  const streamingRefreshTimeoutRef = import_react91.useRef(null);
90734
91121
  const refreshDerivedStreaming = import_react91.useCallback(() => {
90735
91122
  if (streamingRefreshTimeoutRef.current) {
@@ -90825,6 +91212,31 @@ function App2({
90825
91212
  lastPlanFilePathRef.current = planFilePath;
90826
91213
  } catch {}
90827
91214
  }, [currentApproval]);
91215
+ import_react91.useEffect(() => {
91216
+ if (!currentApproval)
91217
+ return;
91218
+ if (currentApproval.toolName === "ExitPlanMode")
91219
+ return;
91220
+ const toolCallId = currentApproval.toolCallId;
91221
+ if (!toolCallId)
91222
+ return;
91223
+ if (eagerCommittedPreviewsRef.current.has(toolCallId))
91224
+ return;
91225
+ if (!currentApprovalShouldCommitPreview)
91226
+ return;
91227
+ const previewItem = {
91228
+ kind: "approval_preview",
91229
+ id: `approval-preview-${toolCallId}`,
91230
+ toolCallId,
91231
+ toolName: currentApproval.toolName,
91232
+ toolArgs: currentApproval.toolArgs || "{}"
91233
+ };
91234
+ if ((isFileEditTool(currentApproval.toolName) || isFileWriteTool(currentApproval.toolName)) && precomputedDiffsRef.current.has(toolCallId)) {
91235
+ previewItem.precomputedDiff = precomputedDiffsRef.current.get(toolCallId);
91236
+ }
91237
+ setStaticItems((prev) => [...prev, previewItem]);
91238
+ eagerCommittedPreviewsRef.current.add(toolCallId);
91239
+ }, [currentApproval, currentApprovalShouldCommitPreview]);
90828
91240
  import_react91.useEffect(() => {
90829
91241
  if (loadingState === "ready" && messageHistory.length > 0 && !hasBackfilledRef.current) {
90830
91242
  hasBackfilledRef.current = true;
@@ -90886,7 +91298,7 @@ function App2({
90886
91298
  });
90887
91299
  buffersRef.current.order.push(statusId);
90888
91300
  refreshDerived();
90889
- commitEligibleLines(buffersRef.current);
91301
+ commitEligibleLines(buffersRef.current, { deferToolCalls: false });
90890
91302
  }
90891
91303
  }, [
90892
91304
  loadingState,
@@ -91303,6 +91715,7 @@ ${newState.originalPrompt}`
91303
91715
  }
91304
91716
  if (isConversationBusyError(errorDetail) && conversationBusyRetriesRef.current < CONVERSATION_BUSY_MAX_RETRIES2) {
91305
91717
  conversationBusyRetriesRef.current += 1;
91718
+ const retryDelayMs = CONVERSATION_BUSY_RETRY_BASE_DELAY_MS * 2 ** (conversationBusyRetriesRef.current - 1);
91306
91719
  const statusId = uid4("status");
91307
91720
  buffersRef.current.byId.set(statusId, {
91308
91721
  kind: "status",
@@ -91313,7 +91726,7 @@ ${newState.originalPrompt}`
91313
91726
  refreshDerived();
91314
91727
  let cancelled = false;
91315
91728
  const startTime = Date.now();
91316
- while (Date.now() - startTime < CONVERSATION_BUSY_RETRY_DELAY_MS2) {
91729
+ while (Date.now() - startTime < retryDelayMs) {
91317
91730
  if (abortControllerRef.current?.signal.aborted || userCancelledRef.current) {
91318
91731
  cancelled = true;
91319
91732
  break;
@@ -91616,39 +92029,11 @@ ${feedback}
91616
92029
  refreshDerived();
91617
92030
  return;
91618
92031
  }
91619
- const approvalResults2 = await Promise.all(approvalsToProcess.map(async (approvalItem) => {
91620
- if (!approvalItem.toolName) {
91621
- return {
91622
- approval: approvalItem,
91623
- permission: {
91624
- decision: "deny",
91625
- reason: "Tool call incomplete - missing name or arguments"
91626
- },
91627
- context: null
91628
- };
91629
- }
91630
- const parsedArgs = safeJsonParseOr(approvalItem.toolArgs, {});
91631
- const permission = await checkToolPermission(approvalItem.toolName, parsedArgs);
91632
- const context3 = await analyzeToolApproval(approvalItem.toolName, parsedArgs);
91633
- return { approval: approvalItem, permission, context: context3 };
91634
- }));
91635
- const needsUserInput = [];
91636
- const autoDenied = [];
91637
- const autoAllowed = [];
91638
- for (const ac of approvalResults2) {
91639
- const { approval: approval2, permission } = ac;
91640
- let decision = permission.decision;
91641
- if (alwaysRequiresUserInput(approval2.toolName) && decision === "allow") {
91642
- decision = "ask";
91643
- }
91644
- if (decision === "ask") {
91645
- needsUserInput.push(ac);
91646
- } else if (decision === "deny") {
91647
- autoDenied.push(ac);
91648
- } else {
91649
- autoAllowed.push(ac);
91650
- }
91651
- }
92032
+ const { needsUserInput, autoAllowed, autoDenied } = await classifyApprovals(approvalsToProcess, {
92033
+ getContext: analyzeToolApproval,
92034
+ alwaysRequiresUserInput,
92035
+ missingNameReason: "Tool call incomplete - missing name or arguments"
92036
+ });
91652
92037
  for (const ac of [...autoAllowed, ...needsUserInput]) {
91653
92038
  const toolName = ac.approval.toolName;
91654
92039
  const toolCallId = ac.approval.toolCallId;
@@ -92133,6 +92518,7 @@ ${feedback}
92133
92518
  ]);
92134
92519
  const handleExit = import_react91.useCallback(async () => {
92135
92520
  saveLastAgentBeforeExit();
92521
+ await runEndHooks();
92136
92522
  const stats = sessionStatsRef.current.getSnapshot();
92137
92523
  telemetry2.trackSessionEnd(stats, "exit_command");
92138
92524
  await telemetry2.flush();
@@ -92140,7 +92526,7 @@ ${feedback}
92140
92526
  setTimeout(() => {
92141
92527
  process.exit(0);
92142
92528
  }, 100);
92143
- }, []);
92529
+ }, [runEndHooks]);
92144
92530
  const handleEnterQueueEditMode = import_react91.useCallback(() => {
92145
92531
  setMessageQueue([]);
92146
92532
  }, []);
@@ -92364,6 +92750,7 @@ ${feedback}
92364
92750
  buffersRef.current.order = [];
92365
92751
  buffersRef.current.tokenCount = 0;
92366
92752
  emittedIdsRef.current.clear();
92753
+ resetDeferredToolCallCommits();
92367
92754
  setStaticItems([]);
92368
92755
  setStaticRenderEpoch((e) => e + 1);
92369
92756
  resetTrajectoryBases();
@@ -92421,6 +92808,7 @@ ${feedback}
92421
92808
  agentName,
92422
92809
  setCommandRunning,
92423
92810
  isAgentBusy,
92811
+ resetDeferredToolCallCommits,
92424
92812
  resetTrajectoryBases
92425
92813
  ]);
92426
92814
  const handleCreateNewAgent = import_react91.useCallback(async (name) => {
@@ -92444,6 +92832,7 @@ ${feedback}
92444
92832
  buffersRef.current.order = [];
92445
92833
  buffersRef.current.tokenCount = 0;
92446
92834
  emittedIdsRef.current.clear();
92835
+ resetDeferredToolCallCommits();
92447
92836
  setStaticItems([]);
92448
92837
  setStaticRenderEpoch((e) => e + 1);
92449
92838
  resetTrajectoryBases();
@@ -92487,7 +92876,13 @@ ${feedback}
92487
92876
  } finally {
92488
92877
  setCommandRunning(false);
92489
92878
  }
92490
- }, [refreshDerived, agentId, setCommandRunning, resetTrajectoryBases]);
92879
+ }, [
92880
+ refreshDerived,
92881
+ agentId,
92882
+ setCommandRunning,
92883
+ resetDeferredToolCallCommits,
92884
+ resetTrajectoryBases
92885
+ ]);
92491
92886
  const handleBashSubmit = import_react91.useCallback(async (command) => {
92492
92887
  if (bashRunning)
92493
92888
  return;
@@ -92592,39 +92987,11 @@ ${expanded.command}` : expanded.command;
92592
92987
  if (!existingApprovals || existingApprovals.length === 0) {
92593
92988
  return { blocked: false };
92594
92989
  }
92595
- const approvalResults2 = await Promise.all(existingApprovals.map(async (approvalItem) => {
92596
- if (!approvalItem.toolName) {
92597
- return {
92598
- approval: approvalItem,
92599
- permission: {
92600
- decision: "deny",
92601
- reason: "Tool call incomplete - missing name"
92602
- },
92603
- context: null
92604
- };
92605
- }
92606
- const parsedArgs = safeJsonParseOr(approvalItem.toolArgs, {});
92607
- const permission = await checkToolPermission(approvalItem.toolName, parsedArgs);
92608
- const context3 = await analyzeToolApproval(approvalItem.toolName, parsedArgs);
92609
- return { approval: approvalItem, permission, context: context3 };
92610
- }));
92611
- const needsUserInput = [];
92612
- const autoAllowed = [];
92613
- const autoDenied = [];
92614
- for (const ac of approvalResults2) {
92615
- const { approval, permission } = ac;
92616
- let decision = permission.decision;
92617
- if (alwaysRequiresUserInput(approval.toolName) && decision === "allow") {
92618
- decision = "ask";
92619
- }
92620
- if (decision === "ask") {
92621
- needsUserInput.push(ac);
92622
- } else if (decision === "deny") {
92623
- autoDenied.push(ac);
92624
- } else {
92625
- autoAllowed.push(ac);
92626
- }
92627
- }
92990
+ const { needsUserInput, autoAllowed, autoDenied } = await classifyApprovals(existingApprovals, {
92991
+ getContext: analyzeToolApproval,
92992
+ alwaysRequiresUserInput,
92993
+ missingNameReason: "Tool call incomplete - missing name"
92994
+ });
92628
92995
  if (needsUserInput.length > 0) {
92629
92996
  setPendingApprovals(needsUserInput.map((ac) => ac.approval));
92630
92997
  setApprovalContexts(needsUserInput.map((ac) => ac.context).filter((ctx) => ctx !== null));
@@ -93181,6 +93548,7 @@ Type your task to begin the loop.`,
93181
93548
  buffersRef.current.order.push(cmdId);
93182
93549
  refreshDerived();
93183
93550
  setCommandRunning(true);
93551
+ await runEndHooks();
93184
93552
  try {
93185
93553
  const client = await getClient2();
93186
93554
  const conversation = await client.conversations.create({
@@ -93194,6 +93562,13 @@ Type your task to begin the loop.`,
93194
93562
  conversationId: conversation.id
93195
93563
  });
93196
93564
  turnCountRef.current = 0;
93565
+ sessionHooksRanRef.current = false;
93566
+ runSessionStartHooks(true, agentId, agentName ?? undefined, conversation.id).then((result2) => {
93567
+ if (result2.feedback.length > 0) {
93568
+ sessionStartFeedbackRef.current = result2.feedback;
93569
+ }
93570
+ }).catch(() => {});
93571
+ sessionHooksRanRef.current = true;
93197
93572
  buffersRef.current.byId.set(cmdId, {
93198
93573
  kind: "command",
93199
93574
  id: cmdId,
@@ -93232,6 +93607,7 @@ Type your task to begin the loop.`,
93232
93607
  buffersRef.current.order.push(cmdId);
93233
93608
  refreshDerived();
93234
93609
  setCommandRunning(true);
93610
+ await runEndHooks();
93235
93611
  try {
93236
93612
  const client = await getClient2();
93237
93613
  await client.agents.messages.reset(agentId, {
@@ -93248,6 +93624,13 @@ Type your task to begin the loop.`,
93248
93624
  conversationId: conversation.id
93249
93625
  });
93250
93626
  turnCountRef.current = 0;
93627
+ sessionHooksRanRef.current = false;
93628
+ runSessionStartHooks(true, agentId, agentName ?? undefined, conversation.id).then((result2) => {
93629
+ if (result2.feedback.length > 0) {
93630
+ sessionStartFeedbackRef.current = result2.feedback;
93631
+ }
93632
+ }).catch(() => {});
93633
+ sessionHooksRanRef.current = true;
93251
93634
  buffersRef.current.byId.set(cmdId, {
93252
93635
  kind: "command",
93253
93636
  id: cmdId,
@@ -93354,14 +93737,29 @@ Type your task to begin the loop.`,
93354
93737
  }
93355
93738
  if (msg.trim().startsWith("/rename")) {
93356
93739
  const parts = msg.trim().split(/\s+/);
93357
- const newName = parts.slice(1).join(" ");
93358
- if (!newName) {
93740
+ const subcommand = parts[1]?.toLowerCase();
93741
+ if (!subcommand || subcommand !== "agent" && subcommand !== "convo") {
93742
+ const cmdId2 = uid4("cmd");
93743
+ buffersRef.current.byId.set(cmdId2, {
93744
+ kind: "command",
93745
+ id: cmdId2,
93746
+ input: msg,
93747
+ output: "Usage: /rename agent <name> or /rename convo <summary>",
93748
+ phase: "finished",
93749
+ success: false
93750
+ });
93751
+ buffersRef.current.order.push(cmdId2);
93752
+ refreshDerived();
93753
+ return { submitted: true };
93754
+ }
93755
+ const newValue = parts.slice(2).join(" ");
93756
+ if (!newValue) {
93359
93757
  const cmdId2 = uid4("cmd");
93360
93758
  buffersRef.current.byId.set(cmdId2, {
93361
93759
  kind: "command",
93362
93760
  id: cmdId2,
93363
93761
  input: msg,
93364
- output: "Please provide a new name: /rename <name>",
93762
+ output: subcommand === "convo" ? "Please provide a summary: /rename convo <summary>" : "Please provide a name: /rename agent <name>",
93365
93763
  phase: "finished",
93366
93764
  success: false
93367
93765
  });
@@ -93369,7 +93767,49 @@ Type your task to begin the loop.`,
93369
93767
  refreshDerived();
93370
93768
  return { submitted: true };
93371
93769
  }
93372
- const validationError = validateAgentName(newName);
93770
+ if (subcommand === "convo") {
93771
+ const cmdId2 = uid4("cmd");
93772
+ buffersRef.current.byId.set(cmdId2, {
93773
+ kind: "command",
93774
+ id: cmdId2,
93775
+ input: msg,
93776
+ output: `Renaming conversation to "${newValue}"...`,
93777
+ phase: "running"
93778
+ });
93779
+ buffersRef.current.order.push(cmdId2);
93780
+ refreshDerived();
93781
+ setCommandRunning(true);
93782
+ try {
93783
+ const client = await getClient2();
93784
+ await client.conversations.update(conversationId, {
93785
+ summary: newValue
93786
+ });
93787
+ buffersRef.current.byId.set(cmdId2, {
93788
+ kind: "command",
93789
+ id: cmdId2,
93790
+ input: msg,
93791
+ output: `Conversation renamed to "${newValue}"`,
93792
+ phase: "finished",
93793
+ success: true
93794
+ });
93795
+ refreshDerived();
93796
+ } catch (error) {
93797
+ const errorDetails = formatErrorDetails(error, agentId);
93798
+ buffersRef.current.byId.set(cmdId2, {
93799
+ kind: "command",
93800
+ id: cmdId2,
93801
+ input: msg,
93802
+ output: `Failed: ${errorDetails}`,
93803
+ phase: "finished",
93804
+ success: false
93805
+ });
93806
+ refreshDerived();
93807
+ } finally {
93808
+ setCommandRunning(false);
93809
+ }
93810
+ return { submitted: true };
93811
+ }
93812
+ const validationError = validateAgentName(newValue);
93373
93813
  if (validationError) {
93374
93814
  const cmdId2 = uid4("cmd");
93375
93815
  buffersRef.current.byId.set(cmdId2, {
@@ -93389,7 +93829,7 @@ Type your task to begin the loop.`,
93389
93829
  kind: "command",
93390
93830
  id: cmdId,
93391
93831
  input: msg,
93392
- output: `Renaming agent to "${newName}"...`,
93832
+ output: `Renaming agent to "${newValue}"...`,
93393
93833
  phase: "running"
93394
93834
  });
93395
93835
  buffersRef.current.order.push(cmdId);
@@ -93397,13 +93837,13 @@ Type your task to begin the loop.`,
93397
93837
  setCommandRunning(true);
93398
93838
  try {
93399
93839
  const client = await getClient2();
93400
- await client.agents.update(agentId, { name: newName });
93401
- updateAgentName(newName);
93840
+ await client.agents.update(agentId, { name: newValue });
93841
+ updateAgentName(newValue);
93402
93842
  buffersRef.current.byId.set(cmdId, {
93403
93843
  kind: "command",
93404
93844
  id: cmdId,
93405
93845
  input: msg,
93406
- output: `Agent renamed to "${newName}"`,
93846
+ output: `Agent renamed to "${newValue}"`,
93407
93847
  phase: "finished",
93408
93848
  success: true
93409
93849
  });
@@ -93529,6 +93969,7 @@ Type your task to begin the loop.`,
93529
93969
  buffersRef.current.order = [];
93530
93970
  buffersRef.current.tokenCount = 0;
93531
93971
  emittedIdsRef.current.clear();
93972
+ resetDeferredToolCallCommits();
93532
93973
  setStaticItems([]);
93533
93974
  setStaticRenderEpoch((e) => e + 1);
93534
93975
  resetTrajectoryBases();
@@ -94357,6 +94798,17 @@ ${SYSTEM_REMINDER_CLOSE}`
94357
94798
  });
94358
94799
  hasSentSessionContextRef.current = true;
94359
94800
  }
94801
+ let sessionStartHookFeedback = "";
94802
+ if (sessionStartFeedbackRef.current.length > 0) {
94803
+ sessionStartHookFeedback = `${SYSTEM_REMINDER_OPEN}
94804
+ [SessionStart hook context]:
94805
+ ${sessionStartFeedbackRef.current.join(`
94806
+ `)}
94807
+ ${SYSTEM_REMINDER_CLOSE}
94808
+
94809
+ `;
94810
+ sessionStartFeedbackRef.current = [];
94811
+ }
94360
94812
  let bashCommandPrefix = "";
94361
94813
  if (bashCommandCacheRef.current.length > 0) {
94362
94814
  bashCommandPrefix = `${SYSTEM_REMINDER_OPEN}
@@ -94419,7 +94871,7 @@ ${SYSTEM_REMINDER_CLOSE}
94419
94871
  `;
94420
94872
  lastNotifiedModeRef.current = currentMode;
94421
94873
  }
94422
- const allReminders = sessionContextReminder + permissionModeAlert + planModeReminder + ralphModeReminder + skillUnloadReminder + bashCommandPrefix + userPromptSubmitHookFeedback + memoryReminderContent + memfsConflictReminder;
94874
+ const allReminders = sessionContextReminder + sessionStartHookFeedback + permissionModeAlert + planModeReminder + ralphModeReminder + skillUnloadReminder + bashCommandPrefix + userPromptSubmitHookFeedback + memoryReminderContent + memfsConflictReminder;
94423
94875
  const messageContent = allReminders && typeof contentParts === "string" ? allReminders + contentParts : Array.isArray(contentParts) && allReminders ? [{ type: "text", text: allReminders }, ...contentParts] : contentParts;
94424
94876
  const userId = uid4("user");
94425
94877
  buffersRef.current.byId.set(userId, {
@@ -94467,22 +94919,11 @@ ${SYSTEM_REMINDER_CLOSE}
94467
94919
  return { submitted: false };
94468
94920
  }
94469
94921
  if (existingApprovals && existingApprovals.length > 0) {
94470
- const approvalResults2 = await Promise.all(existingApprovals.map(async (approvalItem) => {
94471
- if (!approvalItem.toolName) {
94472
- return {
94473
- approval: approvalItem,
94474
- permission: {
94475
- decision: "deny",
94476
- reason: "Tool call incomplete - missing name"
94477
- },
94478
- context: null
94479
- };
94480
- }
94481
- const parsedArgs = safeJsonParseOr(approvalItem.toolArgs, {});
94482
- const permission = await checkToolPermission(approvalItem.toolName, parsedArgs);
94483
- const context3 = await analyzeToolApproval(approvalItem.toolName, parsedArgs);
94484
- return { approval: approvalItem, permission, context: context3 };
94485
- }));
94922
+ const { needsUserInput, autoAllowed, autoDenied } = await classifyApprovals(existingApprovals, {
94923
+ getContext: analyzeToolApproval,
94924
+ alwaysRequiresUserInput,
94925
+ missingNameReason: "Tool call incomplete - missing name"
94926
+ });
94486
94927
  if (userCancelledRef.current || abortControllerRef.current?.signal.aborted) {
94487
94928
  buffersRef.current.byId.delete(userId);
94488
94929
  const orderIndex = buffersRef.current.order.indexOf(userId);
@@ -94493,23 +94934,6 @@ ${SYSTEM_REMINDER_CLOSE}
94493
94934
  refreshDerived();
94494
94935
  return { submitted: false };
94495
94936
  }
94496
- const needsUserInput = [];
94497
- const autoAllowed = [];
94498
- const autoDenied = [];
94499
- for (const ac of approvalResults2) {
94500
- const { approval, permission } = ac;
94501
- let decision = permission.decision;
94502
- if (alwaysRequiresUserInput(approval.toolName) && decision === "allow") {
94503
- decision = "ask";
94504
- }
94505
- if (decision === "ask") {
94506
- needsUserInput.push(ac);
94507
- } else if (decision === "deny") {
94508
- autoDenied.push(ac);
94509
- } else {
94510
- autoAllowed.push(ac);
94511
- }
94512
- }
94513
94937
  if (needsUserInput.length === 0) {
94514
94938
  for (const ac of [...autoAllowed, ...needsUserInput]) {
94515
94939
  const toolName = ac.approval.toolName;
@@ -94925,8 +95349,12 @@ ${SYSTEM_REMINDER_CLOSE}
94925
95349
  ...approvalResultsSnapshot,
94926
95350
  ...additionalDecision ? [additionalDecision] : []
94927
95351
  ];
94928
- executingToolCallIdsRef.current = allDecisions.filter((decision) => decision.type === "approve").map((decision) => decision.approval.toolCallId);
94929
- setToolCallsRunning(buffersRef.current, allDecisions.filter((d) => d.type === "approve").map((d) => d.approval.toolCallId));
95352
+ const approvedDecisions = allDecisions.filter((decision) => decision.type === "approve");
95353
+ const runningDecisions = approvedDecisions.filter((decision) => !decision.precomputedResult);
95354
+ executingToolCallIdsRef.current = runningDecisions.map((decision) => decision.approval.toolCallId);
95355
+ if (runningDecisions.length > 0) {
95356
+ setToolCallsRunning(buffersRef.current, runningDecisions.map((d) => d.approval.toolCallId));
95357
+ }
94930
95358
  refreshDerived();
94931
95359
  const { executeApprovalBatch: executeApprovalBatch2 } = await init_approval_execution().then(() => exports_approval_execution);
94932
95360
  sessionStatsRef.current.startTrajectory();
@@ -95278,7 +95706,7 @@ ${SYSTEM_REMINDER_CLOSE}
95278
95706
  let selectedModel = models2.find((m) => m.id === modelId);
95279
95707
  if (!selectedModel && modelId.includes("/")) {
95280
95708
  const { getModelContextWindow: getModelContextWindow2 } = await init_available_models().then(() => exports_available_models);
95281
- const apiContextWindow = getModelContextWindow2(modelId);
95709
+ const apiContextWindow = await getModelContextWindow2(modelId);
95282
95710
  selectedModel = {
95283
95711
  id: modelId,
95284
95712
  handle: modelId,
@@ -95942,9 +96370,6 @@ Consider switching to a different system prompt using /system to match.` : null;
95942
96370
  });
95943
96371
  setThinkingMessage(getRandomThinkingVerb());
95944
96372
  refreshDerived();
95945
- if (approval.toolCallId) {
95946
- eagerCommittedPreviewsRef.current.add(approval.toolCallId);
95947
- }
95948
96373
  const decision = {
95949
96374
  type: "approve",
95950
96375
  approval,
@@ -96031,6 +96456,8 @@ Plan file path: ${planFilePath}`;
96031
96456
  return lines.filter((ln) => {
96032
96457
  if (!("phase" in ln))
96033
96458
  return false;
96459
+ if (emittedIdsRef.current.has(ln.id))
96460
+ return false;
96034
96461
  if (ln.kind === "command" || ln.kind === "bash_command") {
96035
96462
  return ln.phase === "running";
96036
96463
  }
@@ -96038,7 +96465,7 @@ Plan file path: ${planFilePath}`;
96038
96465
  if (ln.name && isTaskTool(ln.name)) {
96039
96466
  return ln.phase === "ready" || ln.phase === "streaming";
96040
96467
  }
96041
- return ln.phase !== "finished";
96468
+ return ln.phase !== "finished" || deferredToolCallCommitsRef.current.has(ln.id);
96042
96469
  }
96043
96470
  if (ln.kind === "event") {
96044
96471
  return ln.phase === "running";
@@ -96047,7 +96474,7 @@ Plan file path: ${planFilePath}`;
96047
96474
  return false;
96048
96475
  return ln.phase === "streaming";
96049
96476
  });
96050
- }, [lines, tokenStreamingEnabled]);
96477
+ }, [lines, tokenStreamingEnabled, staticItems.length, deferredCommitAt]);
96051
96478
  const { agents: subagents } = import_react91.useSyncExternalStore(subscribe2, getSnapshot2);
96052
96479
  const shouldAnimate = import_react91.useMemo(() => {
96053
96480
  const countLines4 = (text) => {
@@ -96125,7 +96552,7 @@ Plan file path: ${planFilePath}`;
96125
96552
  });
96126
96553
  buffersRef.current.order.push(statusId);
96127
96554
  refreshDerived();
96128
- commitEligibleLines(buffersRef.current);
96555
+ commitEligibleLines(buffersRef.current, { deferToolCalls: false });
96129
96556
  }
96130
96557
  }, [
96131
96558
  loadingState,
@@ -96146,6 +96573,8 @@ Plan file path: ${planFilePath}`;
96146
96573
  const trajectoryTokenDisplay = Math.max(liveTrajectoryTokenBase + runTokenDelta, trajectoryTokenDisplayRef.current);
96147
96574
  const inputVisible = !showExitStats;
96148
96575
  const inputEnabled = !showExitStats && pendingApprovals.length === 0 && !anySelectorOpen;
96576
+ const currentApprovalPreviewCommitted = currentApproval?.toolCallId ? eagerCommittedPreviewsRef.current.has(currentApproval.toolCallId) : false;
96577
+ const showApprovalPreview = !currentApprovalShouldCommitPreview && !currentApprovalPreviewCommitted;
96149
96578
  import_react91.useEffect(() => {
96150
96579
  trajectoryTokenDisplayRef.current = trajectoryTokenDisplay;
96151
96580
  }, [trajectoryTokenDisplay]);
@@ -96155,51 +96584,53 @@ Plan file path: ${planFilePath}`;
96155
96584
  /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Static, {
96156
96585
  items: staticItems,
96157
96586
  style: { flexDirection: "column" },
96158
- children: (item, index) => /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Box_default, {
96159
- marginTop: index > 0 ? 1 : 0,
96160
- children: item.kind === "welcome" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(WelcomeScreen, {
96161
- loadingState: "ready",
96162
- ...item.snapshot
96163
- }, undefined, false, undefined, this) : item.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(UserMessage, {
96164
- line: item
96165
- }, undefined, false, undefined, this) : item.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ReasoningMessage, {
96166
- line: item
96167
- }, undefined, false, undefined, this) : item.kind === "assistant" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(AssistantMessage, {
96168
- line: item
96169
- }, undefined, false, undefined, this) : item.kind === "tool_call" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ToolCallMessage, {
96170
- line: item,
96171
- precomputedDiffs: precomputedDiffsRef.current,
96172
- lastPlanFilePath: lastPlanFilePathRef.current
96173
- }, undefined, false, undefined, this) : item.kind === "subagent_group" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(SubagentGroupStatic, {
96174
- agents: item.agents
96175
- }, undefined, false, undefined, this) : item.kind === "error" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ErrorMessage, {
96176
- line: item
96177
- }, undefined, false, undefined, this) : item.kind === "status" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(StatusMessage, {
96178
- line: item
96179
- }, undefined, false, undefined, this) : item.kind === "event" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(EventMessage, {
96180
- line: item
96181
- }, undefined, false, undefined, this) : item.kind === "separator" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Box_default, {
96182
- marginTop: 1,
96183
- children: /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Text2, {
96184
- dimColor: true,
96185
- children: "─".repeat(columns)
96186
- }, undefined, false, undefined, this)
96187
- }, undefined, false, undefined, this) : item.kind === "command" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(CommandMessage, {
96188
- line: item
96189
- }, undefined, false, undefined, this) : item.kind === "bash_command" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(BashCommandMessage, {
96190
- line: item
96191
- }, undefined, false, undefined, this) : item.kind === "trajectory_summary" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(TrajectorySummary, {
96192
- line: item
96193
- }, undefined, false, undefined, this) : item.kind === "approval_preview" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ApprovalPreview, {
96194
- toolName: item.toolName,
96195
- toolArgs: item.toolArgs,
96196
- precomputedDiff: item.precomputedDiff,
96197
- allDiffs: precomputedDiffsRef.current,
96198
- planContent: item.planContent,
96199
- planFilePath: item.planFilePath,
96200
- toolCallId: item.toolCallId
96201
- }, undefined, false, undefined, this) : null
96202
- }, item.id, false, undefined, this)
96587
+ children: (item, index) => {
96588
+ return /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Box_default, {
96589
+ marginTop: index > 0 ? 1 : 0,
96590
+ children: item.kind === "welcome" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(WelcomeScreen, {
96591
+ loadingState: "ready",
96592
+ ...item.snapshot
96593
+ }, undefined, false, undefined, this) : item.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(UserMessage, {
96594
+ line: item
96595
+ }, undefined, false, undefined, this) : item.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ReasoningMessage, {
96596
+ line: item
96597
+ }, undefined, false, undefined, this) : item.kind === "assistant" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(AssistantMessage, {
96598
+ line: item
96599
+ }, undefined, false, undefined, this) : item.kind === "tool_call" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ToolCallMessage, {
96600
+ line: item,
96601
+ precomputedDiffs: precomputedDiffsRef.current,
96602
+ lastPlanFilePath: lastPlanFilePathRef.current
96603
+ }, undefined, false, undefined, this) : item.kind === "subagent_group" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(SubagentGroupStatic, {
96604
+ agents: item.agents
96605
+ }, undefined, false, undefined, this) : item.kind === "error" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ErrorMessage, {
96606
+ line: item
96607
+ }, undefined, false, undefined, this) : item.kind === "status" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(StatusMessage, {
96608
+ line: item
96609
+ }, undefined, false, undefined, this) : item.kind === "event" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(EventMessage, {
96610
+ line: item
96611
+ }, undefined, false, undefined, this) : item.kind === "separator" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Box_default, {
96612
+ marginTop: 1,
96613
+ children: /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Text2, {
96614
+ dimColor: true,
96615
+ children: "─".repeat(columns)
96616
+ }, undefined, false, undefined, this)
96617
+ }, undefined, false, undefined, this) : item.kind === "command" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(CommandMessage, {
96618
+ line: item
96619
+ }, undefined, false, undefined, this) : item.kind === "bash_command" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(BashCommandMessage, {
96620
+ line: item
96621
+ }, undefined, false, undefined, this) : item.kind === "trajectory_summary" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(TrajectorySummary, {
96622
+ line: item
96623
+ }, undefined, false, undefined, this) : item.kind === "approval_preview" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ApprovalPreview, {
96624
+ toolName: item.toolName,
96625
+ toolArgs: item.toolArgs,
96626
+ precomputedDiff: item.precomputedDiff,
96627
+ allDiffs: precomputedDiffsRef.current,
96628
+ planContent: item.planContent,
96629
+ planFilePath: item.planFilePath,
96630
+ toolCallId: item.toolCallId
96631
+ }, undefined, false, undefined, this) : null
96632
+ }, item.id, false, undefined, this);
96633
+ }
96203
96634
  }, staticRenderEpoch, false, undefined, this),
96204
96635
  /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Box_default, {
96205
96636
  flexDirection: "column",
@@ -96217,10 +96648,12 @@ Plan file path: ${planFilePath}`;
96217
96648
  liveItems.length > 0 && /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(Box_default, {
96218
96649
  flexDirection: "column",
96219
96650
  children: liveItems.map((ln) => {
96220
- if (ln.kind === "tool_call" && ln.name && isTaskTool(ln.name) && ln.toolCallId && !pendingIds.has(ln.toolCallId) && ln.toolCallId !== currentApproval?.toolCallId) {
96651
+ const isFileTool = ln.kind === "tool_call" && ln.name && (isFileEditTool(ln.name) || isFileWriteTool(ln.name) || isPatchTool(ln.name));
96652
+ const isApprovalTracked = ln.kind === "tool_call" && ln.toolCallId && (ln.toolCallId === currentApproval?.toolCallId || pendingIds.has(ln.toolCallId) || queuedIds.has(ln.toolCallId));
96653
+ if (isFileTool && !isApprovalTracked) {
96221
96654
  return null;
96222
96655
  }
96223
- if (ln.kind === "tool_call" && ln.toolCallId && eagerCommittedPreviewsRef.current.has(ln.toolCallId) && ln.toolCallId !== currentApproval?.toolCallId) {
96656
+ if (ln.kind === "tool_call" && ln.name && isTaskTool(ln.name) && ln.toolCallId && !pendingIds.has(ln.toolCallId) && ln.toolCallId !== currentApproval?.toolCallId) {
96224
96657
  return null;
96225
96658
  }
96226
96659
  const matchesCurrentApproval = ln.kind === "tool_call" && currentApproval && ln.toolCallId === currentApproval.toolCallId;
@@ -96242,7 +96675,8 @@ Plan file path: ${planFilePath}`;
96242
96675
  allDiffs: precomputedDiffsRef.current,
96243
96676
  isFocused: true,
96244
96677
  approveAlwaysText: currentApprovalContext?.approveAlwaysText,
96245
- allowPersistence: currentApprovalContext?.allowPersistence ?? true
96678
+ allowPersistence: currentApprovalContext?.allowPersistence ?? true,
96679
+ showPreview: showApprovalPreview
96246
96680
  }, undefined, false, undefined, this) : ln.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(UserMessage, {
96247
96681
  line: ln
96248
96682
  }, undefined, false, undefined, this) : ln.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ReasoningMessage, {
@@ -96291,7 +96725,8 @@ Plan file path: ${planFilePath}`;
96291
96725
  allDiffs: precomputedDiffsRef.current,
96292
96726
  isFocused: true,
96293
96727
  approveAlwaysText: currentApprovalContext?.approveAlwaysText,
96294
- allowPersistence: currentApprovalContext?.allowPersistence ?? true
96728
+ allowPersistence: currentApprovalContext?.allowPersistence ?? true,
96729
+ showPreview: showApprovalPreview
96295
96730
  }, undefined, false, undefined, this)
96296
96731
  }, undefined, false, undefined, this),
96297
96732
  /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(SubagentGroupDisplay, {}, undefined, false, undefined, this)
@@ -96543,6 +96978,7 @@ Plan file path: ${planFilePath}`;
96543
96978
  buffersRef.current.order = [];
96544
96979
  buffersRef.current.tokenCount = 0;
96545
96980
  emittedIdsRef.current.clear();
96981
+ resetDeferredToolCallCommits();
96546
96982
  setStaticItems([]);
96547
96983
  setStaticRenderEpoch((e) => e + 1);
96548
96984
  resetTrajectoryBases();
@@ -96665,6 +97101,7 @@ Plan file path: ${planFilePath}`;
96665
97101
  buffersRef.current.order = [];
96666
97102
  buffersRef.current.tokenCount = 0;
96667
97103
  emittedIdsRef.current.clear();
97104
+ resetDeferredToolCallCommits();
96668
97105
  setStaticItems([]);
96669
97106
  setStaticRenderEpoch((e) => e + 1);
96670
97107
  resetTrajectoryBases();
@@ -96764,6 +97201,7 @@ Plan file path: ${planFilePath}`;
96764
97201
  buffersRef.current.order = [];
96765
97202
  buffersRef.current.tokenCount = 0;
96766
97203
  emittedIdsRef.current.clear();
97204
+ resetDeferredToolCallCommits();
96767
97205
  setStaticItems([]);
96768
97206
  setStaticRenderEpoch((e) => e + 1);
96769
97207
  resetTrajectoryBases();
@@ -96982,7 +97420,7 @@ Open /mcp to attach or detach tools for this server.`,
96982
97420
  ]
96983
97421
  }, resumeKey, true, undefined, this);
96984
97422
  }
96985
- var import_react91, jsx_dev_runtime69, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H", MIN_RESIZE_DELTA = 2, EAGER_CANCEL = true, LLM_API_ERROR_MAX_RETRIES2 = 3, CONVERSATION_BUSY_MAX_RETRIES2 = 1, CONVERSATION_BUSY_RETRY_DELAY_MS2 = 2500, INTERRUPT_MESSAGE = "Interrupted – tell the agent what to do differently. Something went wrong? Use /feedback to report issues.", ERROR_FEEDBACK_HINT = "Something went wrong? Use /feedback to report issues.", OPUS_BEDROCK_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to Bedrock Opus 4.5", PROVIDER_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to another provider", INTERACTIVE_SLASH_COMMANDS, NON_STATE_COMMANDS;
97423
+ var import_react91, jsx_dev_runtime69, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H", MIN_RESIZE_DELTA = 2, TOOL_CALL_COMMIT_DEFER_MS = 50, EAGER_CANCEL = true, LLM_API_ERROR_MAX_RETRIES2 = 3, CONVERSATION_BUSY_MAX_RETRIES2 = 3, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 2500, INTERRUPT_MESSAGE = "Interrupted – tell the agent what to do differently. Something went wrong? Use /feedback to report issues.", ERROR_FEEDBACK_HINT = "Something went wrong? Use /feedback to report issues.", OPUS_BEDROCK_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to Bedrock Opus 4.5", PROVIDER_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to another provider", INTERACTIVE_SLASH_COMMANDS, NON_STATE_COMMANDS, APPROVAL_OPTIONS_HEIGHT = 8, APPROVAL_PREVIEW_BUFFER = 4, MIN_WRAP_WIDTH = 10, TEXT_WRAP_GUTTER = 6, DIFF_WRAP_GUTTER = 12;
96986
97424
  var init_App2 = __esm(async () => {
96987
97425
  init_error();
96988
97426
  init_check_approval();
@@ -96995,6 +97433,7 @@ var init_App2 = __esm(async () => {
96995
97433
  init_mode();
96996
97434
  init_mode2();
96997
97435
  init_settings();
97436
+ init_debug();
96998
97437
  init_colors();
96999
97438
  init_SessionStats();
97000
97439
  init_AnimationContext();
@@ -97062,6 +97501,7 @@ var init_App2 = __esm(async () => {
97062
97501
  init_UserMessageRich(),
97063
97502
  init_WelcomeScreen(),
97064
97503
  init_accumulator(),
97504
+ init_approvalClassification(),
97065
97505
  init_memoryReminder(),
97066
97506
  init_sessionContext(),
97067
97507
  init_stream(),
@@ -97853,7 +98293,7 @@ async function createAgent2(nameOrOptions = DEFAULT_AGENT_NAME, model, embedding
97853
98293
  blockProvenance.push({ label: blockId, source: "shared" });
97854
98294
  }
97855
98295
  const modelUpdateArgs = getModelUpdateArgs(modelHandle);
97856
- const contextWindow = modelUpdateArgs?.context_window;
98296
+ const contextWindow = modelUpdateArgs?.context_window ?? await getModelContextWindow(modelHandle);
97857
98297
  let systemPromptContent;
97858
98298
  if (options.systemPromptCustom) {
97859
98299
  systemPromptContent = options.systemPromptCustom;
@@ -97926,6 +98366,7 @@ var init_create3 = __esm(async () => {
97926
98366
  init_promptAssets();
97927
98367
  init_skills2();
97928
98368
  await __promiseAll([
98369
+ init_available_models(),
97929
98370
  init_client2(),
97930
98371
  init_modify()
97931
98372
  ]);
@@ -98069,7 +98510,7 @@ function buildModelSettings2(modelHandle, updateArgs) {
98069
98510
  async function updateAgentLLMConfig2(agentId, modelHandle, updateArgs) {
98070
98511
  const client = await getClient2();
98071
98512
  const modelSettings = buildModelSettings2(modelHandle, updateArgs);
98072
- const contextWindow = updateArgs?.context_window;
98513
+ const contextWindow = updateArgs?.context_window ?? await getModelContextWindow(modelHandle);
98073
98514
  const hasModelSettings = Object.keys(modelSettings).length > 0;
98074
98515
  await client.agents.update(agentId, {
98075
98516
  model: modelHandle,
@@ -98151,6 +98592,7 @@ async function updateAgentSystemPromptMemfs2(agentId, enableMemfs) {
98151
98592
  var init_modify2 = __esm(async () => {
98152
98593
  await __promiseAll([
98153
98594
  init_openai_codex_provider(),
98595
+ init_available_models(),
98154
98596
  init_client2()
98155
98597
  ]);
98156
98598
  });
@@ -98247,6 +98689,7 @@ class InternalServerError extends APIError {
98247
98689
 
98248
98690
  // src/agent/check-approval.ts
98249
98691
  init_error();
98692
+ init_debug();
98250
98693
  var MESSAGE_HISTORY_LIMIT = 15;
98251
98694
  function isBackfillEnabled() {
98252
98695
  const val = process.env.LETTA_BACKFILL;
@@ -100815,6 +101258,7 @@ class PermissionModeManager {
100815
101258
  var permissionMode = new PermissionModeManager;
100816
101259
 
100817
101260
  // src/settings-manager.ts
101261
+ init_debug();
100818
101262
  init_fs();
100819
101263
  await init_secrets();
100820
101264
  import { homedir as homedir6 } from "node:os";
@@ -101843,6 +102287,7 @@ var telemetry = new TelemetryManager;
101843
102287
  init_model();
101844
102288
  init_subagents();
101845
102289
  init_constants();
102290
+ init_debug();
101846
102291
  await __promiseAll([
101847
102292
  init_approval_execution(),
101848
102293
  init_hooks(),
@@ -103415,4 +103860,4 @@ Error during initialization: ${message}`);
103415
103860
  }
103416
103861
  main();
103417
103862
 
103418
- //# debugId=ECD33F3765BB3B7864756E2164756E21
103863
+ //# debugId=B1589B59DC2C974664756E2164756E21