@pensar/apex 2.4.0 → 2.5.0-canary.aa559068

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 (68) hide show
  1. package/build/agent-48xj79bj.js +20 -0
  2. package/build/{agent-nhs1w5w0.js → agent-7q3c9cjt.js} +11 -10
  3. package/build/{agent-5ejt5vpz.js → agent-ve8pkr6q.js} +9 -8
  4. package/build/{apps-17wvz1d7.js → apps-4rsrnftx.js} +72 -31
  5. package/build/{auth-z3h9859n.js → auth-0w07dbg2.js} +25 -20
  6. package/build/authentication-87zzmjdd.js +20 -0
  7. package/build/blackboxAgent-x0e1wqjx.js +20 -0
  8. package/build/blackboxPentest-0hwj49dr.js +70 -0
  9. package/build/{cli-32z0017y.js → cli-12ky98ey.js} +2 -2
  10. package/build/{cli-5k33webc.js → cli-2ar3m569.js} +45 -43
  11. package/build/cli-53rm0rrz.js +15 -0
  12. package/build/{cli-72cvcqhg.js → cli-54egv3xw.js} +1 -1
  13. package/build/cli-68260ckj.js +12136 -0
  14. package/build/{cli-gq40s5b3.js → cli-6cq67y5m.js} +5 -3
  15. package/build/{cli-mavved45.js → cli-7735d17s.js} +46 -25
  16. package/build/cli-7q3ae3ft.js +6 -0
  17. package/build/{cli-tznv8pf1.js → cli-8mws7sa5.js} +1 -1
  18. package/build/cli-9pbk8m32.js +32 -0
  19. package/build/{cli-swpbx60z.js → cli-avmhkjtv.js} +1 -1481
  20. package/build/{cli-9rhzhgx2.js → cli-eaaqeve1.js} +1 -1
  21. package/build/{cli-qqt4gj1w.js → cli-g0whajsq.js} +5 -3
  22. package/build/{cli-vq52kp7j.js → cli-gb1s7k3v.js} +17 -5
  23. package/build/{cli-2cbwdk78.js → cli-h8xsqggy.js} +1 -1
  24. package/build/{cli-0n4r54hq.js → cli-jsw8vt43.js} +1 -1
  25. package/build/cli-knd276ed.js +1485 -0
  26. package/build/{cli-repbyhkk.js → cli-kya26m6f.js} +1136 -525
  27. package/build/{cli-k8kc47pq.js → cli-mmyn6wmb.js} +3 -3
  28. package/build/{cli-tkc598ey.js → cli-qwv4jsj2.js} +371 -279
  29. package/build/{cli-xshasbwe.js → cli-r3w58pmq.js} +30 -4
  30. package/build/{cli-vnynf0js.js → cli-w51z9qjr.js} +10 -4
  31. package/build/{cli-b1adytsy.js → cli-whp0f8gp.js} +28 -5
  32. package/build/cli.js +553 -95
  33. package/build/{config-f1mj4nhd.js → config-z9k13rvy.js} +18 -15
  34. package/build/{doctor-ekneewa3.js → doctor-tc5zs6pn.js} +8 -7
  35. package/build/{fastStrike-8zka43xs.js → fastStrike-ssm7df42.js} +13 -10
  36. package/build/{fixes-b89s5haw.js → fixes-av9nq0hq.js} +25 -20
  37. package/build/getMachineId-bsd-xj3bn2fv.js +36 -0
  38. package/build/getMachineId-darwin-07jzjcx6.js +36 -0
  39. package/build/getMachineId-linux-ahngynxd.js +29 -0
  40. package/build/getMachineId-unsupported-vy43b44z.js +19 -0
  41. package/build/getMachineId-win-8x66cynz.js +38 -0
  42. package/build/{index-hqjdg6fg.js → index-1ppbwgf9.js} +17 -9
  43. package/build/{index-mtq7kmxz.js → index-5wh7gq37.js} +4 -2
  44. package/build/{index-6d22pyxt.js → index-718m7jw9.js} +1669 -1193
  45. package/build/{index-rbzhk712.js → index-gng8ebqf.js} +11 -10
  46. package/build/{index-y5w6n3tb.js → index-hhcjyc6j.js} +2 -2
  47. package/build/{index-sf16jm0m.js → index-mffgf5hg.js} +9 -7
  48. package/build/{index-ghwf5z3v.js → index-p1atm3ha.js} +3 -3
  49. package/build/{index-4ad3sdwk.js → index-vvkvwmyr.js} +4 -4
  50. package/build/{issues-xx5bs4md.js → issues-hfg8jydv.js} +72 -26
  51. package/build/{logs-qtxe51w2.js → logs-0rd1z7ct.js} +25 -20
  52. package/build/{offesecAgent-b9e2xm6v.js → offesecAgent-j4epj93p.js} +10 -9
  53. package/build/pentest-dp9w88pw.js +30 -0
  54. package/build/{pentests-6ks3szc4.js → pentests-enyxjtg6.js} +26 -21
  55. package/build/{targetedPentest-s1ptvq8e.js → targetedPentest-kwabfy86.js} +11 -10
  56. package/build/{targets-sxygyj87.js → targets-7x0pcmws.js} +27 -22
  57. package/build/threatModel-zwk5eb6r.js +29 -0
  58. package/build/{uninstall-tvsh81v8.js → uninstall-psm9btpt.js} +6 -3
  59. package/build/{upload-7wdyyqmr.js → upload-dybnknjs.js} +8 -7
  60. package/build/{utils-7fwdkqk6.js → utils-dsfk965b.js} +7 -6
  61. package/package.json +8 -2
  62. package/build/agent-mvpgvgks.js +0 -19
  63. package/build/authentication-3s97cpk4.js +0 -19
  64. package/build/blackboxAgent-gw0a1ca4.js +0 -19
  65. package/build/blackboxPentest-44333zss.js +0 -37
  66. package/build/main-mmjp338j.js +0 -324
  67. package/build/pentest-xtc1sjdw.js +0 -29
  68. package/build/threatModel-xsm6fwk5.js +0 -27
@@ -7,23 +7,36 @@ import {
7
7
  buildThreatModelPrompt,
8
8
  createSkillsRegistry,
9
9
  runOffensiveSecurityAgent
10
- } from "./cli-vq52kp7j.js";
10
+ } from "./cli-gb1s7k3v.js";
11
+ import {
12
+ EMPTY_SESSION_TOKEN_USAGE,
13
+ accumulateSessionTokens,
14
+ contextPercentage
15
+ } from "./cli-9pbk8m32.js";
11
16
  import {
12
17
  convertModelMessagesToUI,
13
18
  loadSubagents,
14
19
  readExecutionMetrics,
15
20
  writeExecutionMetrics
16
- } from "./cli-5k33webc.js";
17
- import"./cli-gq40s5b3.js";
21
+ } from "./cli-2ar3m569.js";
22
+ import"./cli-mmyn6wmb.js";
23
+ import"./cli-6cq67y5m.js";
24
+ import"./cli-7735d17s.js";
18
25
  import {
19
26
  REPORT_FILENAME_MD
20
- } from "./cli-b1adytsy.js";
21
- import"./cli-mavved45.js";
22
- import"./cli-xshasbwe.js";
23
- import"./cli-32z0017y.js";
24
- import"./cli-k8kc47pq.js";
27
+ } from "./cli-whp0f8gp.js";
28
+ import {
29
+ createWeaveTracer,
30
+ resolveConfig
31
+ } from "./cli-h8xsqggy.js";
32
+ import {
33
+ AVAILABLE_PROVIDERS,
34
+ init_types
35
+ } from "./cli-ee3axgts.js";
36
+ import"./cli-r3w58pmq.js";
37
+ import"./cli-12ky98ey.js";
25
38
  import"./cli-9fsre5pt.js";
26
- import"./cli-qqt4gj1w.js";
39
+ import"./cli-g0whajsq.js";
27
40
  import {
28
41
  ALL_TOOL_NAMES,
29
42
  ASK_USER_QUESTIONS_TOOL_NAME,
@@ -39,10 +52,19 @@ import {
39
52
  hasPlan,
40
53
  planFilePath,
41
54
  readPlan
42
- } from "./cli-repbyhkk.js";
55
+ } from "./cli-kya26m6f.js";
43
56
  import {
44
57
  detectOSAndEnhancePrompt
45
- } from "./cli-tznv8pf1.js";
58
+ } from "./cli-8mws7sa5.js";
59
+ import {
60
+ require_index_node_http,
61
+ require_index_shim,
62
+ require_src as require_src2,
63
+ require_src1 as require_src3,
64
+ require_src2 as require_src4,
65
+ require_src3 as require_src5,
66
+ require_src4 as require_src6
67
+ } from "./cli-68260ckj.js";
46
68
  import {
47
69
  formatParseError,
48
70
  parseHeaderLine
@@ -55,19 +77,15 @@ import {
55
77
  getAutoPopulatedPorts,
56
78
  parseTargetUrl
57
79
  } from "./cli-c8131c4q.js";
58
- import {
59
- createWeaveTracer,
60
- resolveConfig
61
- } from "./cli-2cbwdk78.js";
62
- import {
63
- AVAILABLE_PROVIDERS,
64
- init_types
65
- } from "./cli-ee3axgts.js";
66
80
  import {
67
81
  AVAILABLE_MODELS,
68
82
  DEFAULT_OPENAI_REASONING_EFFORT,
83
+ addRecentModelId,
69
84
  buildAuthConfig,
85
+ getContextWindow,
70
86
  getOpenAIReasoningEfforts,
87
+ getRecentModels,
88
+ getResumeMessages,
71
89
  hasToolCall,
72
90
  init_ai,
73
91
  init_dist,
@@ -85,7 +103,7 @@ import {
85
103
  stepCountIs,
86
104
  write,
87
105
  writeErrorLog
88
- } from "./cli-tkc598ey.js";
106
+ } from "./cli-qwv4jsj2.js";
89
107
  import {
90
108
  disconnect,
91
109
  ensureValidToken,
@@ -100,21 +118,23 @@ import {
100
118
  selectWorkspace,
101
119
  startDeviceFlow,
102
120
  validateGateway
103
- } from "./cli-0n4r54hq.js";
104
- import"./cli-swpbx60z.js";
121
+ } from "./cli-jsw8vt43.js";
122
+ import"./cli-avmhkjtv.js";
105
123
  import"./cli-j54qa6tm.js";
106
124
  import {
107
125
  config,
108
- init_config as init_config2
109
- } from "./cli-72cvcqhg.js";
110
- import {
111
- init_config,
112
- update
113
- } from "./cli-9rhzhgx2.js";
126
+ init_config
127
+ } from "./cli-54egv3xw.js";
128
+ import"./cli-eaaqeve1.js";
114
129
  import {
115
130
  checkForUpdate,
116
- init_installation
117
- } from "./cli-vnynf0js.js";
131
+ init_installation,
132
+ init_package,
133
+ version
134
+ } from "./cli-w51z9qjr.js";
135
+ import {
136
+ require_src
137
+ } from "./cli-knd276ed.js";
118
138
  import {
119
139
  __commonJS,
120
140
  __esm,
@@ -752,11 +772,11 @@ var require_core = __commonJS(function(exports, module) {
752
772
  var warn = (message, ...args) => {
753
773
  console.log(`WARN: ${message}`, ...args);
754
774
  };
755
- var deprecated = (version2, message) => {
756
- if (seenDeprecations[`${version2}/${message}`])
775
+ var deprecated = (version3, message) => {
776
+ if (seenDeprecations[`${version3}/${message}`])
757
777
  return;
758
- console.log(`Deprecated as of ${version2}. ${message}`);
759
- seenDeprecations[`${version2}/${message}`] = true;
778
+ console.log(`Deprecated as of ${version3}. ${message}`);
779
+ seenDeprecations[`${version3}/${message}`] = true;
760
780
  };
761
781
  var MultiClassError = new Error;
762
782
  function remapScopeNames(mode, regexes, { key }) {
@@ -1006,7 +1026,7 @@ var require_core = __commonJS(function(exports, module) {
1006
1026
  }
1007
1027
  return mode;
1008
1028
  }
1009
- var version = "11.11.1";
1029
+ var version2 = "11.11.1";
1010
1030
 
1011
1031
  class HTMLInjectionError extends Error {
1012
1032
  constructor(reason, html) {
@@ -1070,13 +1090,13 @@ https://github.com/highlightjs/highlight.js/issues/2277`);
1070
1090
  if (ignoreIllegals === undefined) {
1071
1091
  ignoreIllegals = true;
1072
1092
  }
1073
- const context = {
1093
+ const context2 = {
1074
1094
  code,
1075
1095
  language: languageName
1076
1096
  };
1077
- fire("before:highlight", context);
1078
- const result = context.result ? context.result : _highlight(context.language, context.code, ignoreIllegals);
1079
- result.code = context.code;
1097
+ fire("before:highlight", context2);
1098
+ const result = context2.result ? context2.result : _highlight(context2.language, context2.code, ignoreIllegals);
1099
+ result.code = context2.code;
1080
1100
  fire("after:highlight", result);
1081
1101
  return result;
1082
1102
  }
@@ -1628,7 +1648,7 @@ https://github.com/highlightjs/highlight.js/issues/2277`);
1628
1648
  hljs.safeMode = function() {
1629
1649
  SAFE_MODE = true;
1630
1650
  };
1631
- hljs.versionString = version;
1651
+ hljs.versionString = version2;
1632
1652
  hljs.regex = {
1633
1653
  concat,
1634
1654
  lookahead,
@@ -49370,13 +49390,110 @@ var require_lib = __commonJS(function(exports, module) {
49370
49390
  });
49371
49391
 
49372
49392
  // src/tui/index.tsx
49373
- init_config2();
49393
+ init_config();
49374
49394
  init_installation();
49375
49395
  init_logger();
49376
49396
  import { createCliRenderer, FrameBufferRenderable, RGBA as RGBA25 } from "@opentui/core";
49377
49397
  import { createRoot, extend } from "@opentui/react";
49378
49398
  import { useEffect as useEffect27, useState as useState40 } from "react";
49379
49399
 
49400
+ // src/core/observability/runtime.ts
49401
+ init_package();
49402
+ var import_api = __toESM(require_src(), 1);
49403
+ var import_context_async_hooks = __toESM(require_src2(), 1);
49404
+ var import_exporter_trace_otlp_http = __toESM(require_src6(), 1);
49405
+ var import_otlp_exporter_base = __toESM(require_src3(), 1);
49406
+ var import_node_http = __toESM(require_index_node_http(), 1);
49407
+ var import_otlp_transformer = __toESM(require_src5(), 1);
49408
+ var import_resources = __toESM(require_src4(), 1);
49409
+ var import_sdk_trace_base = __toESM(require_index_shim(), 1);
49410
+ import { randomUUID } from "node:crypto";
49411
+ var NOOP_RUNTIME = {
49412
+ async forceFlush() {},
49413
+ async shutdown() {}
49414
+ };
49415
+ function isOtelSdkDisabled(env) {
49416
+ return env.OTEL_SDK_DISABLED === "true";
49417
+ }
49418
+ function resolveTracesEndpoint(env) {
49419
+ const specific = env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT?.trim();
49420
+ if (specific)
49421
+ return specific;
49422
+ const generic = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
49423
+ if (!generic)
49424
+ return null;
49425
+ return `${generic.endsWith("/") ? generic : `${generic}/`}v1/traces`;
49426
+ }
49427
+ function resolveTracesProtocol(env) {
49428
+ const raw = (env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? env.OTEL_EXPORTER_OTLP_PROTOCOL ?? "").trim();
49429
+ return raw === "http/protobuf" ? "http/protobuf" : "http/json";
49430
+ }
49431
+ function parseResourceAttributes(env) {
49432
+ const raw = env.OTEL_RESOURCE_ATTRIBUTES;
49433
+ if (!raw)
49434
+ return {};
49435
+ const attrs = {};
49436
+ for (const pair of raw.split(",")) {
49437
+ const eq = pair.indexOf("=");
49438
+ if (eq <= 0)
49439
+ continue;
49440
+ const key = pair.slice(0, eq).trim();
49441
+ const value = pair.slice(eq + 1).trim();
49442
+ if (key && value)
49443
+ attrs[key] = value;
49444
+ }
49445
+ return attrs;
49446
+ }
49447
+ function buildResource(env) {
49448
+ const resourceAttributes = parseResourceAttributes(env);
49449
+ return import_resources.resourceFromAttributes({
49450
+ "service.version": version,
49451
+ "service.instance.id": randomUUID(),
49452
+ ...resourceAttributes,
49453
+ "service.name": env.OTEL_SERVICE_NAME?.trim() || resourceAttributes["service.name"] || "apex"
49454
+ });
49455
+ }
49456
+ function createTraceExporter(protocol) {
49457
+ if (protocol === "http/json") {
49458
+ return new import_exporter_trace_otlp_http.OTLPTraceExporter;
49459
+ }
49460
+ return new import_otlp_exporter_base.OTLPExporterBase(import_node_http.createOtlpHttpExportDelegate(import_node_http.convertLegacyHttpOptions({}, "TRACES", "v1/traces", {
49461
+ "Content-Type": "application/x-protobuf"
49462
+ }), import_otlp_transformer.ProtobufTraceSerializer, "otlp_http_span_exporter", import_otlp_transformer.TraceExporterMetricsHelper, undefined));
49463
+ }
49464
+ var activeRuntime = null;
49465
+ function startObservabilityRuntime(env = process.env) {
49466
+ if (activeRuntime)
49467
+ return activeRuntime;
49468
+ if (isOtelSdkDisabled(env))
49469
+ return NOOP_RUNTIME;
49470
+ if (!resolveTracesEndpoint(env))
49471
+ return NOOP_RUNTIME;
49472
+ const exporter = createTraceExporter(resolveTracesProtocol(env));
49473
+ const provider = new import_sdk_trace_base.BasicTracerProvider({
49474
+ resource: buildResource(env),
49475
+ spanProcessors: [new import_sdk_trace_base.BatchSpanProcessor(exporter)]
49476
+ });
49477
+ const contextManager = new import_context_async_hooks.AsyncLocalStorageContextManager;
49478
+ contextManager.enable();
49479
+ import_api.trace.setGlobalTracerProvider(provider);
49480
+ import_api.context.setGlobalContextManager(contextManager);
49481
+ const runtime = {
49482
+ async forceFlush() {
49483
+ await provider.forceFlush();
49484
+ },
49485
+ async shutdown() {
49486
+ activeRuntime = null;
49487
+ await provider.shutdown();
49488
+ contextManager.disable();
49489
+ import_api.trace.disable();
49490
+ import_api.context.disable();
49491
+ }
49492
+ };
49493
+ activeRuntime = runtime;
49494
+ return runtime;
49495
+ }
49496
+
49380
49497
  // src/core/providers/index.ts
49381
49498
  init_types();
49382
49499
  init_utils();
@@ -52796,16 +52913,15 @@ function createClipboardManager(renderer) {
52796
52913
 
52797
52914
  // src/tui/components/chat/index.tsx
52798
52915
  init_session();
52799
- import { useCallback as useCallback12, useEffect as useEffect12, useState as useState19 } from "react";
52916
+ import { useCallback as useCallback14, useEffect as useEffect12, useState as useState19 } from "react";
52800
52917
 
52801
52918
  // src/tui/context/agent.tsx
52802
52919
  init_ai();
52803
- init_config();
52804
52920
  init_logger();
52805
52921
  init_utils();
52806
52922
  import {
52807
52923
  createContext as createContext3,
52808
- useCallback as useCallback2,
52924
+ useCallback as useCallback4,
52809
52925
  useContext as useContext3,
52810
52926
  useEffect as useEffect2,
52811
52927
  useMemo as useMemo3,
@@ -52814,9 +52930,10 @@ import {
52814
52930
  } from "react";
52815
52931
 
52816
52932
  // src/tui/context/config.tsx
52817
- init_config2();
52933
+ init_config();
52818
52934
  import {
52819
52935
  createContext as createContext2,
52936
+ useCallback as useCallback2,
52820
52937
  useContext as useContext2,
52821
52938
  useMemo as useMemo2,
52822
52939
  useState as useState2
@@ -52825,20 +52942,22 @@ import { jsxDEV as jsxDEV2 } from "@opentui/react/jsx-dev-runtime";
52825
52942
  var ctx = createContext2(null);
52826
52943
  function ConfigProvider({ children, config: config2 }) {
52827
52944
  const [appConfig, setAppConfig] = useState2(config2);
52945
+ const update = useCallback2(async (newConfig) => {
52946
+ await config.update(newConfig);
52947
+ setAppConfig((current) => ({
52948
+ ...current,
52949
+ ...newConfig
52950
+ }));
52951
+ }, []);
52952
+ const reload = useCallback2(async () => {
52953
+ const freshConfig = await config.get();
52954
+ setAppConfig(freshConfig);
52955
+ }, []);
52828
52956
  const value = useMemo2(() => ({
52829
52957
  data: appConfig,
52830
- update: async (newConfig) => {
52831
- await config.update(newConfig);
52832
- setAppConfig({
52833
- ...appConfig,
52834
- ...newConfig
52835
- });
52836
- },
52837
- reload: async () => {
52838
- const freshConfig = await config.get();
52839
- setAppConfig(freshConfig);
52840
- }
52841
- }), [appConfig]);
52958
+ update,
52959
+ reload
52960
+ }), [appConfig, reload, update]);
52842
52961
  return /* @__PURE__ */ jsxDEV2(ctx.Provider, {
52843
52962
  value,
52844
52963
  children
@@ -52852,15 +52971,133 @@ var useConfig = () => {
52852
52971
  return config2;
52853
52972
  };
52854
52973
 
52974
+ // src/tui/context/session-usage.ts
52975
+ import { useCallback as useCallback3, useSyncExternalStore } from "react";
52976
+ var EMPTY_STATE = {
52977
+ tokenUsage: EMPTY_SESSION_TOKEN_USAGE,
52978
+ contextUsage: null
52979
+ };
52980
+
52981
+ class SessionUsageStore {
52982
+ sessions = new Map;
52983
+ listeners = new Map;
52984
+ activeListeners = new Set;
52985
+ activeId = null;
52986
+ provisionalState = EMPTY_STATE;
52987
+ get activeSessionId() {
52988
+ return this.activeId;
52989
+ }
52990
+ beginNewSession() {
52991
+ const changed = this.activeId !== null || this.provisionalState !== EMPTY_STATE;
52992
+ this.activeId = null;
52993
+ this.provisionalState = EMPTY_STATE;
52994
+ if (changed)
52995
+ this.emitActive();
52996
+ }
52997
+ enterSession(sessionId, seed) {
52998
+ if (!this.sessions.has(sessionId)) {
52999
+ this.sessions.set(sessionId, {
53000
+ tokenUsage: seed?.tokenUsage ?? this.provisionalState.tokenUsage,
53001
+ contextUsage: seed?.contextUsage ?? null
53002
+ });
53003
+ this.provisionalState = EMPTY_STATE;
53004
+ }
53005
+ if (this.activeId !== sessionId) {
53006
+ this.activeId = sessionId;
53007
+ this.emit(sessionId);
53008
+ }
53009
+ }
53010
+ forgetSession(sessionId) {
53011
+ this.sessions.delete(sessionId);
53012
+ this.listeners.delete(sessionId);
53013
+ if (this.activeId === sessionId)
53014
+ this.activeId = null;
53015
+ }
53016
+ getSnapshot(sessionId) {
53017
+ if (sessionId === null)
53018
+ return EMPTY_STATE;
53019
+ return this.sessions.get(sessionId) ?? EMPTY_STATE;
53020
+ }
53021
+ subscribe(sessionId, listener) {
53022
+ let set = this.listeners.get(sessionId);
53023
+ if (!set) {
53024
+ set = new Set;
53025
+ this.listeners.set(sessionId, set);
53026
+ }
53027
+ set.add(listener);
53028
+ return () => {
53029
+ set.delete(listener);
53030
+ };
53031
+ }
53032
+ addSessionTokens(sessionId, step) {
53033
+ if (sessionId === null) {
53034
+ const next = accumulateSessionTokens(this.provisionalState.tokenUsage, step);
53035
+ if (next !== this.provisionalState.tokenUsage) {
53036
+ this.provisionalState = { tokenUsage: next, contextUsage: null };
53037
+ if (this.activeId === null)
53038
+ this.emitActive();
53039
+ }
53040
+ return;
53041
+ }
53042
+ this.update(sessionId, (state) => ({
53043
+ ...state,
53044
+ tokenUsage: accumulateSessionTokens(state.tokenUsage, step)
53045
+ }));
53046
+ }
53047
+ setRootContext(sessionId, sample) {
53048
+ if (sessionId === null)
53049
+ return;
53050
+ this.update(sessionId, (state) => ({
53051
+ ...state,
53052
+ contextUsage: sample
53053
+ }));
53054
+ }
53055
+ update(sessionId, updater) {
53056
+ const current = this.sessions.get(sessionId);
53057
+ if (!current)
53058
+ return;
53059
+ const next = updater(current);
53060
+ if (next === current || next.tokenUsage === current.tokenUsage && next.contextUsage === current.contextUsage) {
53061
+ return;
53062
+ }
53063
+ this.sessions.set(sessionId, next);
53064
+ this.emit(sessionId);
53065
+ }
53066
+ getActiveSnapshot() {
53067
+ return this.activeId === null ? this.provisionalState : this.getSnapshot(this.activeId);
53068
+ }
53069
+ subscribeActive(listener) {
53070
+ this.activeListeners.add(listener);
53071
+ return () => {
53072
+ this.activeListeners.delete(listener);
53073
+ };
53074
+ }
53075
+ emit(sessionId) {
53076
+ for (const listener of this.listeners.get(sessionId) ?? [])
53077
+ listener();
53078
+ if (sessionId === this.activeId)
53079
+ this.emitActive();
53080
+ }
53081
+ emitActive() {
53082
+ for (const listener of this.activeListeners)
53083
+ listener();
53084
+ }
53085
+ }
53086
+ function useActiveSessionUsage(store) {
53087
+ const subscribe = useCallback3((listener) => store.subscribeActive(listener), [store]);
53088
+ const getSnapshot = useCallback3(() => store.getActiveSnapshot(), [store]);
53089
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
53090
+ }
53091
+
52855
53092
  // src/tui/context/agent.tsx
52856
53093
  import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
52857
53094
  var AgentContext = createContext3(null);
52858
53095
  function useAgent() {
52859
- const context = useContext3(AgentContext);
52860
- if (!context) {
53096
+ const context2 = useContext3(AgentContext);
53097
+ if (!context2) {
52861
53098
  throw new Error("useAgent must be used within AgentProvider");
52862
53099
  }
52863
- return context;
53100
+ return context2;
52864
53101
  }
52865
53102
  function AgentProvider({ children }) {
52866
53103
  const appConfig = useConfig();
@@ -52868,13 +53105,7 @@ function AgentProvider({ children }) {
52868
53105
  return getDefaultModelForConfig(appConfig.data) ?? AVAILABLE_MODELS[0];
52869
53106
  });
52870
53107
  const [isModelUserSelected, setIsModelUserSelected] = useState3(false);
52871
- const [tokenUsage, setTokenUsage] = useState3({
52872
- inputTokens: 0,
52873
- outputTokens: 0,
52874
- totalTokens: 0,
52875
- cachedTokens: 0,
52876
- cacheWriteTokens: 0
52877
- });
53108
+ const usageStore = useMemo3(() => new SessionUsageStore, []);
52878
53109
  const [hasExecuted, setHasExecuted] = useState3(false);
52879
53110
  const [thinking, setThinking] = useState3(false);
52880
53111
  const [reasoningEnabled, setReasoningEnabledInternal] = useState3(() => appConfig.data?.reasoningEnabled ?? false);
@@ -52885,12 +53116,17 @@ function AgentProvider({ children }) {
52885
53116
  reasoningEnabledRef.current = reasoningEnabled;
52886
53117
  const openAIReasoningEffortRef = useRef(openAIReasoningEffort);
52887
53118
  openAIReasoningEffortRef.current = openAIReasoningEffort;
52888
- const setModel = useCallback2((newModel, persist = true) => {
53119
+ const recentModelIdsRef = useRef(appConfig.data.recentModelIds ?? []);
53120
+ const updateConfig = appConfig.update;
53121
+ const setModel = useCallback4((newModel, persist = true) => {
52889
53122
  setModelInternal(newModel);
52890
53123
  if (persist) {
52891
53124
  setIsModelUserSelected(true);
53125
+ const nextRecentModelIds = addRecentModelId(recentModelIdsRef.current, newModel.id);
53126
+ recentModelIdsRef.current = nextRecentModelIds;
52892
53127
  const configUpdate = {
52893
- selectedModelId: newModel.id
53128
+ selectedModelId: newModel.id,
53129
+ recentModelIds: nextRecentModelIds
52894
53130
  };
52895
53131
  if (reasoningEnabledRef.current && !modelSupportsThinking(newModel.id)) {
52896
53132
  configUpdate.reasoningEnabled = false;
@@ -52901,23 +53137,26 @@ function AgentProvider({ children }) {
52901
53137
  configUpdate.openAIReasoningEffort = DEFAULT_OPENAI_REASONING_EFFORT;
52902
53138
  setOpenAIReasoningEffortInternal(DEFAULT_OPENAI_REASONING_EFFORT);
52903
53139
  }
52904
- update(configUpdate).catch((err) => {
53140
+ updateConfig(configUpdate).catch((err) => {
52905
53141
  writeErrorLog(err, "AGENT_CONTEXT");
52906
53142
  });
52907
53143
  }
52908
- }, []);
52909
- const setReasoningEnabled = useCallback2((enabled) => {
53144
+ }, [updateConfig]);
53145
+ const setReasoningEnabled = useCallback4((enabled) => {
52910
53146
  setReasoningEnabledInternal(enabled);
52911
- update({ reasoningEnabled: enabled }).catch((err) => {
53147
+ updateConfig({ reasoningEnabled: enabled }).catch((err) => {
52912
53148
  writeErrorLog(err, "AGENT_CONTEXT");
52913
53149
  });
52914
- }, []);
52915
- const setOpenAIReasoningEffort = useCallback2((effort) => {
53150
+ }, [updateConfig]);
53151
+ const setOpenAIReasoningEffort = useCallback4((effort) => {
52916
53152
  setOpenAIReasoningEffortInternal(effort);
52917
- update({ openAIReasoningEffort: effort }).catch((err) => {
53153
+ updateConfig({ openAIReasoningEffort: effort }).catch((err) => {
52918
53154
  writeErrorLog(err, "AGENT_CONTEXT");
52919
53155
  });
52920
- }, []);
53156
+ }, [updateConfig]);
53157
+ useEffect2(() => {
53158
+ recentModelIdsRef.current = appConfig.data.recentModelIds ?? [];
53159
+ }, [appConfig.data.recentModelIds]);
52921
53160
  useEffect2(() => {
52922
53161
  if (appConfig.data?.reasoningEnabled != null) {
52923
53162
  setReasoningEnabledInternal(appConfig.data.reasoningEnabled);
@@ -52948,40 +53187,13 @@ function AgentProvider({ children }) {
52948
53187
  setModelInternal(defaultModel);
52949
53188
  }
52950
53189
  }, [appConfig.data, isModelUserSelected]);
52951
- const addTokenUsage = useCallback2((input, output) => {
52952
- setHasExecuted(true);
52953
- setTokenUsage((prev) => ({
52954
- ...prev,
52955
- inputTokens: prev.inputTokens + input,
52956
- outputTokens: prev.outputTokens + output,
52957
- totalTokens: prev.totalTokens + input + output
52958
- }));
52959
- }, []);
52960
- const addCacheUsage = useCallback2((cacheRead, cacheWrite) => {
52961
- setTokenUsage((prev) => ({
52962
- ...prev,
52963
- cachedTokens: prev.cachedTokens + cacheRead,
52964
- cacheWriteTokens: prev.cacheWriteTokens + cacheWrite
52965
- }));
52966
- }, []);
52967
- const resetTokenUsage = useCallback2(() => {
52968
- setHasExecuted(false);
52969
- setTokenUsage({
52970
- inputTokens: 0,
52971
- outputTokens: 0,
52972
- totalTokens: 0,
52973
- cachedTokens: 0,
52974
- cacheWriteTokens: 0
52975
- });
52976
- }, []);
53190
+ const markExecuted = useCallback4(() => setHasExecuted(true), []);
52977
53191
  const contextValue = useMemo3(() => ({
52978
53192
  model,
52979
53193
  setModel,
52980
53194
  isModelUserSelected,
52981
- tokenUsage,
52982
- addTokenUsage,
52983
- addCacheUsage,
52984
- resetTokenUsage,
53195
+ usageStore,
53196
+ markExecuted,
52985
53197
  hasExecuted,
52986
53198
  thinking,
52987
53199
  setThinking,
@@ -52997,7 +53209,6 @@ function AgentProvider({ children }) {
52997
53209
  model,
52998
53210
  setModel,
52999
53211
  isModelUserSelected,
53000
- tokenUsage,
53001
53212
  hasExecuted,
53002
53213
  thinking,
53003
53214
  reasoningEnabled,
@@ -53005,9 +53216,8 @@ function AgentProvider({ children }) {
53005
53216
  openAIReasoningEffort,
53006
53217
  isExecuting,
53007
53218
  sessionCwd,
53008
- addTokenUsage,
53009
- addCacheUsage,
53010
- resetTokenUsage,
53219
+ usageStore,
53220
+ markExecuted,
53011
53221
  setOpenAIReasoningEffort
53012
53222
  ]);
53013
53223
  return /* @__PURE__ */ jsxDEV3(AgentContext.Provider, {
@@ -53019,14 +53229,14 @@ function AgentProvider({ children }) {
53019
53229
  // src/tui/components/chat/config-view.tsx
53020
53230
  init_utils();
53021
53231
  import { useKeyboard as useKeyboard2 } from "@opentui/react";
53022
- import { useCallback as useCallback4, useEffect as useEffect4, useState as useState5 } from "react";
53232
+ import { useCallback as useCallback6, useEffect as useEffect4, useState as useState5 } from "react";
53023
53233
 
53024
53234
  // src/tui/components/model-picker/ModelPicker.tsx
53025
53235
  init_ai();
53026
53236
  init_utils();
53027
53237
  import { useKeyboard } from "@opentui/react";
53028
53238
  import {
53029
- useCallback as useCallback3,
53239
+ useCallback as useCallback5,
53030
53240
  useEffect as useEffect3,
53031
53241
  useMemo as useMemo4,
53032
53242
  useRef as useRef2,
@@ -53094,18 +53304,105 @@ function findChildById(scrollBox, id) {
53094
53304
  return;
53095
53305
  }
53096
53306
 
53097
- // src/tui/components/model-picker/ModelPicker.tsx
53098
- import { jsxDEV as jsxDEV4 } from "@opentui/react/jsx-dev-runtime";
53099
- var providerNames = {
53100
- anthropic: "Claude",
53307
+ // src/tui/components/model-picker/model-navigation.ts
53308
+ function findSelectedModelIndex(navigationItems, selectedModelId) {
53309
+ return navigationItems.findIndex((item) => (item.type === "model" || item.type === "recent-model") && item.model?.id === selectedModelId);
53310
+ }
53311
+ function retainAvailableProviders(expandedProviders, availableProviders) {
53312
+ const retainedProviders = new Set([...expandedProviders].filter((provider) => availableProviders.has(provider)));
53313
+ return retainedProviders.size === expandedProviders.size ? expandedProviders : retainedProviders;
53314
+ }
53315
+ function getSelectedModelRevealKey({
53316
+ availableModels,
53317
+ selectedModel,
53318
+ lastRevealedSelectedModelKey
53319
+ }) {
53320
+ const selectedModelKey = `${selectedModel.provider}:${selectedModel.id}`;
53321
+ if (lastRevealedSelectedModelKey === selectedModelKey)
53322
+ return null;
53323
+ const selectedModelIsAvailable = availableModels.some((model) => model.id === selectedModel.id && model.provider === selectedModel.provider);
53324
+ return selectedModelIsAvailable ? selectedModelKey : null;
53325
+ }
53326
+
53327
+ // src/tui/components/model-picker/model-search.ts
53328
+ var PROVIDER_DISPLAY_NAMES = {
53329
+ anthropic: "Anthropic",
53101
53330
  openai: "OpenAI",
53102
- google: "Gemini",
53331
+ google: "Google",
53103
53332
  openrouter: "OpenRouter",
53104
53333
  concentrate: "Concentrate",
53105
- bedrock: "Bedrock",
53334
+ bedrock: "AWS Bedrock",
53335
+ "bedrock-mantle": "Bedrock Mantle",
53106
53336
  pensar: "Pensar",
53337
+ inception: "Inception",
53107
53338
  local: "Local LLM"
53108
53339
  };
53340
+ function getProviderDisplayName(provider) {
53341
+ return PROVIDER_DISPLAY_NAMES[provider];
53342
+ }
53343
+ function normalizeSearchText(value) {
53344
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
53345
+ }
53346
+ function filterModels(models, searchQuery) {
53347
+ const terms = normalizeSearchText(searchQuery).split(/\s+/).filter(Boolean);
53348
+ if (terms.length === 0)
53349
+ return models;
53350
+ return models.filter((model) => {
53351
+ const searchableText = normalizeSearchText(`${model.name} ${model.id} ${model.provider} ${getProviderDisplayName(model.provider)}`);
53352
+ return terms.every((term) => searchableText.includes(term));
53353
+ });
53354
+ }
53355
+
53356
+ // src/tui/components/model-picker/model-visibility.ts
53357
+ var HIDDEN_MODEL_IDS = {
53358
+ anthropic: new Set([
53359
+ "claude-3-haiku-20240307",
53360
+ "claude-opus-4-0",
53361
+ "claude-opus-4-20250514",
53362
+ "claude-opus-4-1",
53363
+ "claude-opus-4-1-20250805",
53364
+ "claude-sonnet-4-0",
53365
+ "claude-sonnet-4-20250514"
53366
+ ]),
53367
+ openai: new Set([
53368
+ "gpt-5.3-chat-latest",
53369
+ "gpt-5.2-chat-latest",
53370
+ "gpt-5.2-codex",
53371
+ "gpt-5.1-chat-latest",
53372
+ "gpt-5.1-codex-mini",
53373
+ "gpt-5.1-codex",
53374
+ "gpt-5.1-codex-max",
53375
+ "gpt-5-chat-latest",
53376
+ "gpt-5-codex",
53377
+ "gpt-4.1-nano",
53378
+ "gpt-4.1-nano-2025-04-14",
53379
+ "gpt-4o-2024-05-13",
53380
+ "gpt-3.5-turbo",
53381
+ "gpt-3.5-turbo-0125",
53382
+ "gpt-3.5-turbo-1106",
53383
+ "o4-mini",
53384
+ "o4-mini-2025-04-16",
53385
+ "o3-mini",
53386
+ "o3-mini-2025-01-31",
53387
+ "o1",
53388
+ "o1-2024-12-17"
53389
+ ]),
53390
+ google: new Set([
53391
+ "gemini-2.0-flash",
53392
+ "gemini-2.0-flash-001",
53393
+ "gemini-2.0-flash-lite",
53394
+ "gemini-2.0-flash-lite-001",
53395
+ "gemini-3-pro-preview",
53396
+ "gemini-3.1-flash-lite-preview",
53397
+ "gemini-robotics-er-1.5-preview"
53398
+ ])
53399
+ };
53400
+ function getVisiblePickerModels(models, selectedModel) {
53401
+ return models.filter((model) => !HIDDEN_MODEL_IDS[model.provider]?.has(model.id) || model.id === selectedModel?.id && model.provider === selectedModel.provider);
53402
+ }
53403
+
53404
+ // src/tui/components/model-picker/ModelPicker.tsx
53405
+ import { jsxDEV as jsxDEV4, Fragment } from "@opentui/react/jsx-dev-runtime";
53109
53406
  var providerOrder = [
53110
53407
  "pensar",
53111
53408
  "anthropic",
@@ -53114,11 +53411,10 @@ var providerOrder = [
53114
53411
  "openrouter",
53115
53412
  "concentrate",
53116
53413
  "bedrock",
53414
+ "bedrock-mantle",
53415
+ "inception",
53117
53416
  "local"
53118
53417
  ];
53119
- function setsAreEqual(a, b) {
53120
- return a.size === b.size && [...a].every((value) => b.has(value));
53121
- }
53122
53418
  function PickerRow({
53123
53419
  children,
53124
53420
  id,
@@ -53145,6 +53441,8 @@ function getNavItemId(item) {
53145
53441
  return `provider-${item.provider}`;
53146
53442
  if (item.type === "model")
53147
53443
  return `model-${item.model.id}`;
53444
+ if (item.type === "recent-model")
53445
+ return `recent-model-${item.model.id}`;
53148
53446
  if (item.type === "reasoning")
53149
53447
  return "reasoning-toggle";
53150
53448
  if (item.type === "openai-reasoning")
@@ -53169,8 +53467,10 @@ function ModelPicker({
53169
53467
  const scrollBoxRef = useRef2(null);
53170
53468
  const [availableModels, setAvailableModels] = useState4([]);
53171
53469
  const [searchQuery, setSearchQuery] = useState4("");
53172
- const [expandedProviders, setExpandedProviders] = useState4(new Set(["anthropic"]));
53470
+ const [expandedProviders, setExpandedProviders] = useState4(new Set([selectedModel.provider]));
53173
53471
  const [focusedIndex, setFocusedIndex] = useState4(0);
53472
+ const lastRevealedSelectedModelRef = useRef2(null);
53473
+ const lastFocusedSelectedModelIdRef = useRef2(null);
53174
53474
  const [localUrl, setLocalUrl] = useState4(config2?.localModelUrl ?? "");
53175
53475
  const [localModelName, setLocalModelName] = useState4(config2?.localModelName ?? "");
53176
53476
  const [editingLocalField, setEditingLocalField] = useState4(null);
@@ -53183,86 +53483,105 @@ function ModelPicker({
53183
53483
  setAvailableModels([]);
53184
53484
  return;
53185
53485
  }
53186
- setAvailableModels(getAvailableModels(config2));
53187
- }, [config2]);
53486
+ setAvailableModels(getVisiblePickerModels(getAvailableModels(config2), {
53487
+ id: selectedModel.id,
53488
+ provider: selectedModel.provider
53489
+ }));
53490
+ }, [config2, selectedModel.id, selectedModel.provider]);
53188
53491
  useEffect3(() => {
53189
53492
  const availableProviders = new Set(availableModels.map((model) => model.provider));
53190
- setExpandedProviders((prev) => {
53191
- if (availableProviders.size === 0) {
53192
- return prev.size === 0 ? prev : new Set;
53193
- }
53194
- const preservedProviders = new Set([...prev].filter((provider) => availableProviders.has(provider)));
53195
- if (preservedProviders.size > 0) {
53196
- return setsAreEqual(prev, preservedProviders) ? prev : preservedProviders;
53197
- }
53198
- const fallbackProvider = availableProviders.has(selectedModel.provider) ? selectedModel.provider : availableModels[0]?.provider;
53199
- if (!fallbackProvider) {
53200
- return prev.size === 0 ? prev : new Set;
53201
- }
53202
- return prev.size === 1 && prev.has(fallbackProvider) ? prev : new Set([fallbackProvider]);
53493
+ setExpandedProviders((prev) => retainAvailableProviders(prev, availableProviders));
53494
+ }, [availableModels]);
53495
+ useEffect3(() => {
53496
+ const selectedModelKey = getSelectedModelRevealKey({
53497
+ availableModels,
53498
+ selectedModel: {
53499
+ id: selectedModel.id,
53500
+ provider: selectedModel.provider
53501
+ },
53502
+ lastRevealedSelectedModelKey: lastRevealedSelectedModelRef.current
53203
53503
  });
53204
- }, [availableModels, selectedModel.provider]);
53504
+ if (!selectedModelKey)
53505
+ return;
53506
+ lastRevealedSelectedModelRef.current = selectedModelKey;
53507
+ setExpandedProviders((prev) => prev.has(selectedModel.provider) ? prev : new Set([...prev, selectedModel.provider]));
53508
+ }, [availableModels, selectedModel.id, selectedModel.provider]);
53509
+ const filteredModels = useMemo4(() => filterModels(availableModels, searchQuery), [availableModels, searchQuery]);
53510
+ const isSearching = searchQuery.trim().length > 0;
53511
+ const recentModels = useMemo4(() => getRecentModels(availableModels, config2?.recentModelIds ?? []), [availableModels, config2?.recentModelIds]);
53205
53512
  const groupedModels = useMemo4(() => {
53206
53513
  const groups = {};
53207
- const query = searchQuery.toLowerCase().trim();
53208
53514
  for (const m of availableModels) {
53209
- if (query && !m.name.toLowerCase().includes(query) && !m.id.toLowerCase().includes(query)) {
53210
- continue;
53211
- }
53212
53515
  if (!groups[m.provider]) {
53213
53516
  groups[m.provider] = [];
53214
53517
  }
53215
53518
  groups[m.provider].push(m);
53216
53519
  }
53217
53520
  return groups;
53218
- }, [availableModels, searchQuery]);
53521
+ }, [availableModels]);
53219
53522
  const navigationItems = useMemo4(() => {
53220
53523
  const items = [];
53221
- for (const provider of providerOrder) {
53222
- if (provider === "local") {
53223
- items.push({ type: "provider", provider: "local" });
53224
- if (expandedProviders.has("local")) {
53225
- items.push({ type: "local-input", field: "url" });
53226
- items.push({ type: "local-input", field: "model" });
53227
- const localModels = groupedModels.local;
53228
- if (localModels) {
53229
- for (const m of localModels) {
53230
- items.push({ type: "model", model: m });
53524
+ if (isSearching) {
53525
+ for (const model of filteredModels) {
53526
+ items.push({ type: "model", model });
53527
+ }
53528
+ } else {
53529
+ for (const model of recentModels) {
53530
+ items.push({ type: "recent-model", model });
53531
+ }
53532
+ for (const provider of providerOrder) {
53533
+ if (provider === "local") {
53534
+ items.push({ type: "provider", provider: "local" });
53535
+ if (expandedProviders.has("local")) {
53536
+ items.push({ type: "local-input", field: "url" });
53537
+ items.push({ type: "local-input", field: "model" });
53538
+ const localModels = groupedModels.local;
53539
+ if (localModels) {
53540
+ for (const m of localModels) {
53541
+ items.push({ type: "model", model: m });
53542
+ }
53231
53543
  }
53232
53544
  }
53545
+ continue;
53233
53546
  }
53234
- continue;
53235
- }
53236
- const models = groupedModels[provider];
53237
- if (!models || models.length === 0)
53238
- continue;
53239
- items.push({ type: "provider", provider });
53240
- if (expandedProviders.has(provider)) {
53241
- for (const m of models) {
53242
- items.push({ type: "model", model: m });
53547
+ const models = groupedModels[provider];
53548
+ if (!models || models.length === 0)
53549
+ continue;
53550
+ items.push({ type: "provider", provider });
53551
+ if (expandedProviders.has(provider)) {
53552
+ for (const m of models) {
53553
+ items.push({ type: "model", model: m });
53554
+ }
53243
53555
  }
53244
53556
  }
53245
53557
  }
53246
- if (onReasoningToggle && modelSupportsThinking(selectedModel.id)) {
53558
+ if (!isSearching && onReasoningToggle && modelSupportsThinking(selectedModel.id)) {
53247
53559
  items.push({ type: "reasoning" });
53248
53560
  }
53249
- if (onOpenAIReasoningEffortChange && modelSupportsOpenAIReasoning(selectedModel.id)) {
53561
+ if (!isSearching && onOpenAIReasoningEffortChange && modelSupportsOpenAIReasoning(selectedModel.id)) {
53250
53562
  items.push({ type: "openai-reasoning" });
53251
53563
  }
53252
53564
  return items;
53253
53565
  }, [
53254
53566
  groupedModels,
53567
+ filteredModels,
53568
+ recentModels,
53569
+ isSearching,
53255
53570
  expandedProviders,
53256
53571
  onReasoningToggle,
53257
53572
  onOpenAIReasoningEffortChange,
53258
53573
  selectedModel.id
53259
53574
  ]);
53260
53575
  useEffect3(() => {
53261
- const idx = navigationItems.findIndex((item) => item.type === "model" && item.model.id === selectedModel.id);
53262
- if (idx !== -1) {
53263
- setFocusedIndex(idx);
53576
+ if (isSearching || lastFocusedSelectedModelIdRef.current === selectedModel.id) {
53577
+ return;
53264
53578
  }
53265
- }, [selectedModel.id, navigationItems]);
53579
+ const index = findSelectedModelIndex(navigationItems, selectedModel.id);
53580
+ if (index === -1)
53581
+ return;
53582
+ lastFocusedSelectedModelIdRef.current = selectedModel.id;
53583
+ setFocusedIndex(index);
53584
+ }, [isSearching, navigationItems, selectedModel.id]);
53266
53585
  useEffect3(() => {
53267
53586
  setFocusedIndex((prev) => Math.min(prev, Math.max(0, navigationItems.length - 1)));
53268
53587
  }, [navigationItems.length]);
@@ -53272,16 +53591,16 @@ function ModelPicker({
53272
53591
  return;
53273
53592
  scrollToChild(scrollBoxRef.current, getNavItemId(item));
53274
53593
  }, [focusedIndex, navigationItems]);
53275
- const commitLocalConfig = useCallback3((url, modelName) => {
53594
+ const commitLocalConfig = useCallback5((url, modelName) => {
53276
53595
  if (!onConfigUpdate)
53277
53596
  return;
53278
- const update2 = {
53597
+ const update = {
53279
53598
  localModelUrl: url || null,
53280
53599
  localModelName: modelName || null
53281
53600
  };
53282
- onConfigUpdate(update2);
53601
+ onConfigUpdate(update);
53283
53602
  }, [onConfigUpdate]);
53284
- const finishEditing = useCallback3(() => {
53603
+ const finishEditing = useCallback5(() => {
53285
53604
  setEditingLocalField(null);
53286
53605
  commitLocalConfig(localUrl, localModelName);
53287
53606
  }, [localUrl, localModelName, commitLocalConfig]);
@@ -53290,7 +53609,7 @@ function ModelPicker({
53290
53609
  const openAIReasoningEfforts = getOpenAIReasoningEfforts(selectedModel.id);
53291
53610
  const isReasoningFocused = navigationItems[focusedIndex]?.type === "reasoning";
53292
53611
  const isOpenAIReasoningFocused = navigationItems[focusedIndex]?.type === "openai-reasoning";
53293
- const cycleOpenAIReasoningEffort = useCallback3(() => {
53612
+ const cycleOpenAIReasoningEffort = useCallback5(() => {
53294
53613
  if (!onOpenAIReasoningEffortChange || openAIReasoningEfforts.length === 0) {
53295
53614
  return;
53296
53615
  }
@@ -53302,7 +53621,7 @@ function ModelPicker({
53302
53621
  openAIReasoningEffort,
53303
53622
  openAIReasoningEfforts
53304
53623
  ]);
53305
- const handleKeyboard = useCallback3((key) => {
53624
+ const handleKeyboard = useCallback5((key) => {
53306
53625
  if (!focused)
53307
53626
  return false;
53308
53627
  if (editingLocalField) {
@@ -53312,24 +53631,18 @@ function ModelPicker({
53312
53631
  }
53313
53632
  return false;
53314
53633
  }
53315
- if (navigationItems.length === 0)
53316
- return false;
53317
53634
  if (key.name === "up" || key.name === "down") {
53318
- const newIndex = key.name === "up" ? Math.max(0, focusedIndex - 1) : Math.min(navigationItems.length - 1, focusedIndex + 1);
53635
+ if (navigationItems.length === 0)
53636
+ return false;
53637
+ const direction = key.name === "up" ? -1 : 1;
53638
+ const newIndex = (focusedIndex + direction + navigationItems.length) % navigationItems.length;
53319
53639
  setFocusedIndex(newIndex);
53320
- const item = navigationItems[newIndex];
53321
- if (item && item.type === "model") {
53322
- onSelectModel(item.model);
53323
- }
53324
- return true;
53325
- }
53326
- if (key.name === "backspace") {
53327
- setSearchQuery((prev) => prev.slice(0, -1));
53328
53640
  return true;
53329
53641
  }
53330
53642
  if (key.name === "escape") {
53331
53643
  if (searchQuery) {
53332
53644
  setSearchQuery("");
53645
+ setFocusedIndex(0);
53333
53646
  return true;
53334
53647
  }
53335
53648
  if (onCancel) {
@@ -53338,16 +53651,16 @@ function ModelPicker({
53338
53651
  }
53339
53652
  return false;
53340
53653
  }
53341
- if (key.name === "space" && onReasoningToggle && thinkingSupported) {
53654
+ const currentItem = navigationItems[focusedIndex];
53655
+ if (key.name === "space" && currentItem?.type === "reasoning" && onReasoningToggle && thinkingSupported) {
53342
53656
  onReasoningToggle(!reasoningEnabled);
53343
53657
  return true;
53344
53658
  }
53345
- if (key.name === "space" && onOpenAIReasoningEffortChange && openAIReasoningSupported) {
53659
+ if (key.name === "space" && currentItem?.type === "openai-reasoning" && onOpenAIReasoningEffortChange && openAIReasoningSupported) {
53346
53660
  cycleOpenAIReasoningEffort();
53347
53661
  return true;
53348
53662
  }
53349
53663
  if (key.name === "return") {
53350
- const currentItem = navigationItems[focusedIndex];
53351
53664
  if (!currentItem)
53352
53665
  return false;
53353
53666
  if (currentItem.type === "local-input") {
@@ -53369,17 +53682,20 @@ function ModelPicker({
53369
53682
  }
53370
53683
  return next;
53371
53684
  });
53372
- } else {
53685
+ } else if (currentItem.type === "model" || currentItem.type === "recent-model") {
53686
+ onSelectModel(currentItem.model);
53373
53687
  onConfirm?.();
53374
53688
  }
53375
53689
  return true;
53376
53690
  }
53377
53691
  if (key.name === "left" || key.name === "right") {
53378
- const currentItem = navigationItems[focusedIndex];
53379
- if (!currentItem || currentItem.type === "reasoning" || currentItem.type === "openai-reasoning") {
53692
+ if (isSearching)
53693
+ return false;
53694
+ const currentItem2 = navigationItems[focusedIndex];
53695
+ if (!currentItem2 || currentItem2.type === "recent-model" || currentItem2.type === "reasoning" || currentItem2.type === "openai-reasoning") {
53380
53696
  return false;
53381
53697
  }
53382
- const targetProvider = currentItem.type === "provider" ? currentItem.provider : currentItem.type === "model" ? currentItem.model.provider : "local";
53698
+ const targetProvider = currentItem2.type === "provider" ? currentItem2.provider : currentItem2.type === "model" ? currentItem2.model.provider : "local";
53383
53699
  if (key.name === "left") {
53384
53700
  setExpandedProviders((prev) => {
53385
53701
  const next = new Set(prev);
@@ -53395,13 +53711,6 @@ function ModelPicker({
53395
53711
  }
53396
53712
  return true;
53397
53713
  }
53398
- if (key.sequence && key.sequence.length === 1 && /[a-zA-Z0-9\-_.]/.test(key.sequence)) {
53399
- setSearchQuery((prev) => prev + key.sequence);
53400
- if (!searchQuery) {
53401
- setExpandedProviders(new Set(providerOrder));
53402
- }
53403
- return true;
53404
- }
53405
53714
  return false;
53406
53715
  }, [
53407
53716
  focused,
@@ -53418,7 +53727,8 @@ function ModelPicker({
53418
53727
  thinkingSupported,
53419
53728
  onOpenAIReasoningEffortChange,
53420
53729
  openAIReasoningSupported,
53421
- cycleOpenAIReasoningEffort
53730
+ cycleOpenAIReasoningEffort,
53731
+ isSearching
53422
53732
  ]);
53423
53733
  useKeyboard((key) => {
53424
53734
  const handled = handleKeyboard(key);
@@ -53427,7 +53737,11 @@ function ModelPicker({
53427
53737
  }
53428
53738
  });
53429
53739
  const isProviderFocused = (provider) => navigationItems[focusedIndex]?.type === "provider" && navigationItems[focusedIndex].provider === provider;
53430
- const isModelFocused = (modelId) => navigationItems[focusedIndex]?.type === "model" && navigationItems[focusedIndex].model.id === modelId;
53740
+ const isModelFocused = (modelId, recent = false) => {
53741
+ const item = navigationItems[focusedIndex];
53742
+ const expectedType = recent ? "recent-model" : "model";
53743
+ return item?.type === expectedType && item.model.id === modelId;
53744
+ };
53431
53745
  const isLocalFieldFocused = (field) => navigationItems[focusedIndex]?.type === "local-input" && navigationItems[focusedIndex].field === field;
53432
53746
  return /* @__PURE__ */ jsxDEV4("box", {
53433
53747
  flexDirection: "column",
@@ -53438,21 +53752,42 @@ function ModelPicker({
53438
53752
  children: [
53439
53753
  /* @__PURE__ */ jsxDEV4("box", {
53440
53754
  flexShrink: 0,
53441
- children: searchQuery ? /* @__PURE__ */ jsxDEV4(PickerRow, {
53442
- children: /* @__PURE__ */ jsxDEV4("text", {
53443
- fg: colors2.text,
53755
+ marginBottom: 1,
53756
+ children: [
53757
+ /* @__PURE__ */ jsxDEV4(PickerRow, {
53444
53758
  children: [
53445
- "Search: ",
53446
- searchQuery
53759
+ /* @__PURE__ */ jsxDEV4("text", {
53760
+ fg: colors2.primary,
53761
+ children: "Search "
53762
+ }, undefined, false, undefined, this),
53763
+ /* @__PURE__ */ jsxDEV4("input", {
53764
+ flexGrow: 1,
53765
+ value: searchQuery,
53766
+ onInput: (value) => {
53767
+ setSearchQuery(value);
53768
+ setFocusedIndex(0);
53769
+ },
53770
+ focused: focused && editingLocalField === null,
53771
+ placeholder: "family, model name, or provider...",
53772
+ textColor: colors2.text,
53773
+ focusedTextColor: colors2.text,
53774
+ backgroundColor: "transparent",
53775
+ cursorColor: colors2.textMuted
53776
+ }, undefined, false, undefined, this)
53447
53777
  ]
53448
- }, undefined, true, undefined, this)
53449
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV4(PickerRow, {
53450
- children: /* @__PURE__ */ jsxDEV4("text", {
53451
- fg: colors2.textMuted,
53452
- children: "Type to search models..."
53778
+ }, undefined, true, undefined, this),
53779
+ isSearching && /* @__PURE__ */ jsxDEV4(PickerRow, {
53780
+ children: /* @__PURE__ */ jsxDEV4("text", {
53781
+ fg: colors2.primary,
53782
+ children: [
53783
+ "Results (",
53784
+ filteredModels.length,
53785
+ ")"
53786
+ ]
53787
+ }, undefined, true, undefined, this)
53453
53788
  }, undefined, false, undefined, this)
53454
- }, undefined, false, undefined, this)
53455
- }, undefined, false, undefined, this),
53789
+ ]
53790
+ }, undefined, true, undefined, this),
53456
53791
  /* @__PURE__ */ jsxDEV4("scrollbox", {
53457
53792
  ref: scrollBoxRef,
53458
53793
  style: {
@@ -53460,170 +53795,240 @@ function ModelPicker({
53460
53795
  flexShrink: 1,
53461
53796
  width: "100%",
53462
53797
  overflow: "hidden",
53463
- maxHeight: navigationItems.length
53798
+ maxHeight: Math.max(1, navigationItems.length + (!isSearching && recentModels.length > 0 ? 1 : 0))
53464
53799
  },
53465
53800
  contentOptions: {
53466
53801
  flexDirection: "column"
53467
53802
  },
53468
53803
  scrollbarOptions: {
53469
- visible: false
53804
+ trackOptions: {
53805
+ foregroundColor: colors2.textMuted,
53806
+ backgroundColor: colors2.backgroundElement
53807
+ }
53470
53808
  }
53471
53809
  },
53472
53810
  stickyScroll: false,
53473
- children: providerOrder.flatMap((provider) => {
53474
- const isExpanded = expandedProviders.has(provider);
53475
- const providerName = providerNames[provider] || provider;
53476
- const isFocused = isProviderFocused(provider);
53477
- if (provider === "local") {
53478
- const localModels = groupedModels.local;
53479
- const modelCount = localModels?.length ?? 0;
53480
- const elements2 = [];
53481
- elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53482
- id: "provider-local",
53811
+ children: [
53812
+ !isSearching && recentModels.length > 0 && /* @__PURE__ */ jsxDEV4(Fragment, {
53813
+ children: [
53814
+ /* @__PURE__ */ jsxDEV4(PickerRow, {
53815
+ children: /* @__PURE__ */ jsxDEV4("text", {
53816
+ fg: colors2.primary,
53817
+ children: "Recent"
53818
+ }, undefined, false, undefined, this)
53819
+ }, undefined, false, undefined, this),
53820
+ recentModels.map((model) => {
53821
+ const isSelected = model.id === selectedModel.id;
53822
+ const isFocused = isModelFocused(model.id, true);
53823
+ return /* @__PURE__ */ jsxDEV4(PickerRow, {
53824
+ id: `recent-model-${model.id}`,
53825
+ backgroundColor: isFocused ? colors2.backgroundSelected : undefined,
53826
+ children: /* @__PURE__ */ jsxDEV4("text", {
53827
+ children: [
53828
+ /* @__PURE__ */ jsxDEV4("span", {
53829
+ fg: isFocused ? colors2.primary : colors2.text,
53830
+ children: [
53831
+ isSelected ? "●" : "○",
53832
+ " ",
53833
+ model.name
53834
+ ]
53835
+ }, undefined, true, undefined, this),
53836
+ /* @__PURE__ */ jsxDEV4("span", {
53837
+ fg: colors2.textMuted,
53838
+ children: ` ${getProviderDisplayName(model.provider)}`
53839
+ }, undefined, false, undefined, this)
53840
+ ]
53841
+ }, undefined, true, undefined, this)
53842
+ }, `recent-${model.id}`, false, undefined, this);
53843
+ })
53844
+ ]
53845
+ }, undefined, true, undefined, this),
53846
+ isSearching && (filteredModels.length === 0 ? /* @__PURE__ */ jsxDEV4(PickerRow, {
53847
+ children: /* @__PURE__ */ jsxDEV4("text", {
53848
+ fg: colors2.textMuted,
53849
+ children: "No matching models"
53850
+ }, undefined, false, undefined, this)
53851
+ }, undefined, false, undefined, this) : filteredModels.map((model) => {
53852
+ const isSelected = model.id === selectedModel.id;
53853
+ const isFocused = isModelFocused(model.id);
53854
+ return /* @__PURE__ */ jsxDEV4(PickerRow, {
53855
+ id: `model-${model.id}`,
53856
+ backgroundColor: isFocused ? colors2.backgroundSelected : undefined,
53483
53857
  children: /* @__PURE__ */ jsxDEV4("text", {
53484
- fg: isFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
53485
53858
  children: [
53486
- isFocused ? "❯" : isExpanded ? "▾" : "▸",
53487
- " ",
53488
- providerName,
53489
- modelCount > 0 ? ` (${modelCount})` : ""
53859
+ /* @__PURE__ */ jsxDEV4("span", {
53860
+ fg: isFocused ? colors2.primary : colors2.text,
53861
+ children: [
53862
+ isSelected ? "●" : "○",
53863
+ " ",
53864
+ model.name
53865
+ ]
53866
+ }, undefined, true, undefined, this),
53867
+ /* @__PURE__ */ jsxDEV4("span", {
53868
+ fg: colors2.textMuted,
53869
+ children: ` ${getProviderDisplayName(model.provider)}`
53870
+ }, undefined, false, undefined, this)
53490
53871
  ]
53491
53872
  }, undefined, true, undefined, this)
53492
- }, "local", false, undefined, this));
53493
- if (isExpanded) {
53494
- const isUrlFocused = isLocalFieldFocused("url");
53495
- const isUrlEditing = editingLocalField === "url";
53496
- if (isUrlEditing) {
53497
- elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53498
- id: "local-input-url",
53499
- paddingLeft: 2,
53873
+ }, model.id, false, undefined, this);
53874
+ })),
53875
+ providerOrder.flatMap((provider) => {
53876
+ if (isSearching)
53877
+ return [];
53878
+ const isExpanded = expandedProviders.has(provider);
53879
+ const providerName = getProviderDisplayName(provider);
53880
+ const isFocused = isProviderFocused(provider);
53881
+ if (provider === "local") {
53882
+ const localModels = groupedModels.local;
53883
+ const modelCount = localModels?.length ?? 0;
53884
+ const elements2 = [];
53885
+ elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53886
+ id: "provider-local",
53887
+ children: /* @__PURE__ */ jsxDEV4("text", {
53888
+ fg: isFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
53500
53889
  children: [
53501
- /* @__PURE__ */ jsxDEV4("text", {
53502
- fg: colors2.primary,
53503
- children: " URL: "
53504
- }, undefined, false, undefined, this),
53505
- /* @__PURE__ */ jsxDEV4("input", {
53506
- focused: true,
53507
- value: localUrl,
53508
- backgroundColor: "transparent",
53509
- cursorColor: colors2.textMuted,
53510
- onInput: (v) => setLocalUrl(typeof v === "string" ? v : ""),
53511
- onPaste: (event) => {
53512
- const cleaned = getPasteText(event).replace(/\r?\n/g, "");
53513
- setLocalUrl((prev) => `${prev}${cleaned}`);
53514
- },
53515
- onSubmit: finishEditing
53516
- }, undefined, false, undefined, this)
53890
+ isFocused ? "❯" : isExpanded ? "▾" : "▸",
53891
+ " ",
53892
+ providerName,
53893
+ modelCount > 0 ? ` (${modelCount})` : ""
53517
53894
  ]
53518
- }, "local-url", true, undefined, this));
53519
- } else {
53520
- elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53521
- id: "local-input-url",
53522
- paddingLeft: 2,
53523
- children: /* @__PURE__ */ jsxDEV4("text", {
53524
- fg: isUrlFocused ? colors2.primary : colors2.textMuted,
53525
- children: ` URL: ${localUrl || "(press Enter to set)"}`
53526
- }, undefined, false, undefined, this)
53527
- }, "local-url", false, undefined, this));
53528
- }
53529
- const isModelFieldFocused = isLocalFieldFocused("model");
53530
- const isModelEditing = editingLocalField === "model";
53531
- if (isModelEditing) {
53532
- elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53533
- id: "local-input-model",
53534
- paddingLeft: 2,
53535
- children: [
53536
- /* @__PURE__ */ jsxDEV4("text", {
53537
- fg: colors2.primary,
53538
- children: " Model: "
53539
- }, undefined, false, undefined, this),
53540
- /* @__PURE__ */ jsxDEV4("input", {
53541
- focused: true,
53542
- value: localModelName,
53543
- backgroundColor: "transparent",
53544
- cursorColor: colors2.textMuted,
53545
- onInput: (v) => setLocalModelName(typeof v === "string" ? v : ""),
53546
- onPaste: (event) => {
53547
- const cleaned = getPasteText(event).replace(/\r?\n/g, "");
53548
- setLocalModelName((prev) => `${prev}${cleaned}`);
53549
- },
53550
- onSubmit: finishEditing
53895
+ }, undefined, true, undefined, this)
53896
+ }, "local", false, undefined, this));
53897
+ if (isExpanded) {
53898
+ const isUrlFocused = isLocalFieldFocused("url");
53899
+ const isUrlEditing = editingLocalField === "url";
53900
+ if (isUrlEditing) {
53901
+ elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53902
+ id: "local-input-url",
53903
+ paddingLeft: 2,
53904
+ children: [
53905
+ /* @__PURE__ */ jsxDEV4("text", {
53906
+ fg: colors2.primary,
53907
+ children: " URL: "
53908
+ }, undefined, false, undefined, this),
53909
+ /* @__PURE__ */ jsxDEV4("input", {
53910
+ focused: true,
53911
+ value: localUrl,
53912
+ backgroundColor: "transparent",
53913
+ cursorColor: colors2.textMuted,
53914
+ onInput: (v) => setLocalUrl(typeof v === "string" ? v : ""),
53915
+ onPaste: (event) => {
53916
+ const cleaned = getPasteText(event).replace(/\r?\n/g, "");
53917
+ setLocalUrl((prev) => `${prev}${cleaned}`);
53918
+ },
53919
+ onSubmit: finishEditing
53920
+ }, undefined, false, undefined, this)
53921
+ ]
53922
+ }, "local-url", true, undefined, this));
53923
+ } else {
53924
+ elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53925
+ id: "local-input-url",
53926
+ paddingLeft: 2,
53927
+ children: /* @__PURE__ */ jsxDEV4("text", {
53928
+ fg: isUrlFocused ? colors2.primary : colors2.textMuted,
53929
+ children: ` URL: ${localUrl || "(press Enter to set)"}`
53551
53930
  }, undefined, false, undefined, this)
53552
- ]
53553
- }, "local-model", true, undefined, this));
53554
- } else {
53555
- elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53556
- id: "local-input-model",
53557
- paddingLeft: 2,
53558
- children: /* @__PURE__ */ jsxDEV4("text", {
53559
- fg: isModelFieldFocused ? colors2.primary : colors2.textMuted,
53560
- children: ` Model: ${localModelName || "(press Enter to set)"}`
53561
- }, undefined, false, undefined, this)
53562
- }, "local-model", false, undefined, this));
53563
- }
53564
- if (localModels) {
53565
- for (const m of localModels) {
53566
- const isSelected = m.id === selectedModel.id;
53567
- const isMFocused = isModelFocused(m.id);
53931
+ }, "local-url", false, undefined, this));
53932
+ }
53933
+ const isModelFieldFocused = isLocalFieldFocused("model");
53934
+ const isModelEditing = editingLocalField === "model";
53935
+ if (isModelEditing) {
53936
+ elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53937
+ id: "local-input-model",
53938
+ paddingLeft: 2,
53939
+ children: [
53940
+ /* @__PURE__ */ jsxDEV4("text", {
53941
+ fg: colors2.primary,
53942
+ children: " Model: "
53943
+ }, undefined, false, undefined, this),
53944
+ /* @__PURE__ */ jsxDEV4("input", {
53945
+ focused: true,
53946
+ value: localModelName,
53947
+ backgroundColor: "transparent",
53948
+ cursorColor: colors2.textMuted,
53949
+ onInput: (v) => setLocalModelName(typeof v === "string" ? v : ""),
53950
+ onPaste: (event) => {
53951
+ const cleaned = getPasteText(event).replace(/\r?\n/g, "");
53952
+ setLocalModelName((prev) => `${prev}${cleaned}`);
53953
+ },
53954
+ onSubmit: finishEditing
53955
+ }, undefined, false, undefined, this)
53956
+ ]
53957
+ }, "local-model", true, undefined, this));
53958
+ } else {
53568
53959
  elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53569
- id: `model-${m.id}`,
53960
+ id: "local-input-model",
53570
53961
  paddingLeft: 2,
53571
53962
  children: /* @__PURE__ */ jsxDEV4("text", {
53572
- fg: isMFocused ? colors2.primary : colors2.textMuted,
53573
- children: [
53574
- isSelected ? "●" : "○",
53575
- " ",
53576
- m.name
53577
- ]
53578
- }, undefined, true, undefined, this)
53579
- }, m.id, false, undefined, this));
53963
+ fg: isModelFieldFocused ? colors2.primary : colors2.textMuted,
53964
+ children: ` Model: ${localModelName || "(press Enter to set)"}`
53965
+ }, undefined, false, undefined, this)
53966
+ }, "local-model", false, undefined, this));
53967
+ }
53968
+ if (localModels) {
53969
+ for (const m of localModels) {
53970
+ const isSelected = m.id === selectedModel.id;
53971
+ const isMFocused = isModelFocused(m.id);
53972
+ elements2.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53973
+ id: `model-${m.id}`,
53974
+ paddingLeft: 2,
53975
+ children: /* @__PURE__ */ jsxDEV4("text", {
53976
+ fg: isMFocused ? colors2.primary : colors2.textMuted,
53977
+ children: [
53978
+ isSelected ? "●" : "○",
53979
+ " ",
53980
+ m.name
53981
+ ]
53982
+ }, undefined, true, undefined, this)
53983
+ }, m.id, false, undefined, this));
53984
+ }
53580
53985
  }
53581
53986
  }
53987
+ return elements2;
53582
53988
  }
53583
- return elements2;
53584
- }
53585
- const models = groupedModels[provider];
53586
- if (!models || models.length === 0)
53587
- return [];
53588
- const elements = [];
53589
- elements.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53590
- id: `provider-${provider}`,
53591
- children: /* @__PURE__ */ jsxDEV4("text", {
53592
- fg: isFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
53593
- children: [
53594
- isFocused ? "❯" : isExpanded ? "▾" : "▸",
53595
- " ",
53596
- providerName,
53597
- " (",
53598
- models.length,
53599
- ")"
53600
- ]
53601
- }, undefined, true, undefined, this)
53602
- }, provider, false, undefined, this));
53603
- if (isExpanded) {
53604
- for (const m of models) {
53605
- const isSelected = m.id === selectedModel.id;
53606
- const isMFocused = isModelFocused(m.id);
53607
- const isDefault = m.id === "claude-haiku-4-5" || m.id === "gpt-4o-mini";
53608
- elements.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53609
- id: `model-${m.id}`,
53610
- paddingLeft: 2,
53611
- children: /* @__PURE__ */ jsxDEV4("text", {
53612
- fg: isMFocused ? colors2.primary : colors2.textMuted,
53613
- children: [
53614
- isSelected ? "●" : "○",
53615
- " ",
53616
- m.name,
53617
- isDefault && !isModelUserSelected && isSelected ? " [default]" : ""
53618
- ]
53619
- }, undefined, true, undefined, this)
53620
- }, m.id, false, undefined, this));
53989
+ const models = groupedModels[provider];
53990
+ if (!models || models.length === 0)
53991
+ return [];
53992
+ const elements = [];
53993
+ elements.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
53994
+ id: `provider-${provider}`,
53995
+ children: /* @__PURE__ */ jsxDEV4("text", {
53996
+ fg: isFocused ? colors2.primary : isExpanded ? colors2.text : colors2.textMuted,
53997
+ children: [
53998
+ isFocused ? "❯" : isExpanded ? "▾" : "▸",
53999
+ " ",
54000
+ providerName,
54001
+ " (",
54002
+ models.length,
54003
+ ")"
54004
+ ]
54005
+ }, undefined, true, undefined, this)
54006
+ }, provider, false, undefined, this));
54007
+ if (isExpanded) {
54008
+ for (const m of models) {
54009
+ const isSelected = m.id === selectedModel.id;
54010
+ const isMFocused = isModelFocused(m.id);
54011
+ const isDefault = m.id === "claude-haiku-4-5" || m.id === "gpt-4o-mini";
54012
+ elements.push(/* @__PURE__ */ jsxDEV4(PickerRow, {
54013
+ id: `model-${m.id}`,
54014
+ paddingLeft: 2,
54015
+ children: /* @__PURE__ */ jsxDEV4("text", {
54016
+ fg: isMFocused ? colors2.primary : colors2.textMuted,
54017
+ children: [
54018
+ isSelected ? "●" : "○",
54019
+ " ",
54020
+ m.name,
54021
+ isDefault && !isModelUserSelected && isSelected ? " [default]" : ""
54022
+ ]
54023
+ }, undefined, true, undefined, this)
54024
+ }, m.id, false, undefined, this));
54025
+ }
53621
54026
  }
53622
- }
53623
- return elements;
53624
- })
53625
- }, undefined, false, undefined, this),
53626
- onReasoningToggle && thinkingSupported && /* @__PURE__ */ jsxDEV4("box", {
54027
+ return elements;
54028
+ })
54029
+ ]
54030
+ }, isSearching ? "search-results" : "model-browser", true, undefined, this),
54031
+ !isSearching && onReasoningToggle && thinkingSupported && /* @__PURE__ */ jsxDEV4("box", {
53627
54032
  flexShrink: 0,
53628
54033
  paddingTop: 1,
53629
54034
  children: /* @__PURE__ */ jsxDEV4(PickerRow, {
@@ -53637,7 +54042,7 @@ function ModelPicker({
53637
54042
  }, undefined, true, undefined, this)
53638
54043
  }, undefined, false, undefined, this)
53639
54044
  }, undefined, false, undefined, this),
53640
- onOpenAIReasoningEffortChange && openAIReasoningSupported && /* @__PURE__ */ jsxDEV4("box", {
54045
+ !isSearching && onOpenAIReasoningEffortChange && openAIReasoningSupported && /* @__PURE__ */ jsxDEV4("box", {
53641
54046
  flexShrink: 0,
53642
54047
  paddingTop: 1,
53643
54048
  children: /* @__PURE__ */ jsxDEV4(PickerRow, {
@@ -53737,7 +54142,7 @@ function ConfigView({ config: config2, onBack, onStart }) {
53737
54142
  return;
53738
54143
  }
53739
54144
  });
53740
- const handleModelSelect = useCallback4((model) => {
54145
+ const handleModelSelect = useCallback6((model) => {
53741
54146
  setSelectedModel(model);
53742
54147
  setIsModelUserSelected(true);
53743
54148
  }, []);
@@ -53913,7 +54318,7 @@ function isValidUrl(url) {
53913
54318
  }
53914
54319
 
53915
54320
  // src/tui/components/chat/home-view.tsx
53916
- import { useCallback as useCallback11, useEffect as useEffect11, useState as useState18 } from "react";
54321
+ import { useCallback as useCallback13, useEffect as useEffect11, useState as useState18 } from "react";
53917
54322
 
53918
54323
  // src/core/history.ts
53919
54324
  init_storage();
@@ -53970,7 +54375,7 @@ function getEntries() {
53970
54375
  // src/tui/context/command.tsx
53971
54376
  import {
53972
54377
  createContext as createContext7,
53973
- useCallback as useCallback6,
54378
+ useCallback as useCallback8,
53974
54379
  useContext as useContext7,
53975
54380
  useEffect as useEffect5,
53976
54381
  useMemo as useMemo8,
@@ -54365,7 +54770,7 @@ function deobfuscate(input) {
54365
54770
  return output;
54366
54771
  }
54367
54772
  // src/tui/command-registry.ts
54368
- init_config2();
54773
+ init_config();
54369
54774
 
54370
54775
  // src/tui/utils/command-flags.ts
54371
54776
  import { readFileSync } from "node:fs";
@@ -55268,7 +55673,7 @@ var useRoute = () => {
55268
55673
  // src/tui/context/toast.tsx
55269
55674
  import {
55270
55675
  createContext as createContext6,
55271
- useCallback as useCallback5,
55676
+ useCallback as useCallback7,
55272
55677
  useContext as useContext6,
55273
55678
  useMemo as useMemo7,
55274
55679
  useState as useState8
@@ -55283,10 +55688,10 @@ var DEFAULT_DURATION = {
55283
55688
  };
55284
55689
  function ToastProvider({ children }) {
55285
55690
  const [toasts, setToasts] = useState8([]);
55286
- const dismiss = useCallback5((id) => {
55691
+ const dismiss = useCallback7((id) => {
55287
55692
  setToasts((prev) => prev.filter((t) => t.id !== id));
55288
55693
  }, []);
55289
- const toast = useCallback5((message, variant = "default", duration) => {
55694
+ const toast = useCallback7((message, variant = "default", duration) => {
55290
55695
  const id = nextId++;
55291
55696
  const ms = duration ?? DEFAULT_DURATION[variant];
55292
55697
  setToasts((prev) => [...prev, { id, message, variant, duration: ms }]);
@@ -55309,11 +55714,11 @@ function useToast() {
55309
55714
  import { jsxDEV as jsxDEV9 } from "@opentui/react/jsx-dev-runtime";
55310
55715
  var CommandContext = createContext7(null);
55311
55716
  function useCommand() {
55312
- const context = useContext7(CommandContext);
55313
- if (!context) {
55717
+ const context2 = useContext7(CommandContext);
55718
+ if (!context2) {
55314
55719
  throw new Error("useCommand must be used within CommandProvider");
55315
55720
  }
55316
- return context;
55721
+ return context2;
55317
55722
  }
55318
55723
  function CommandProvider({
55319
55724
  children,
@@ -55372,7 +55777,7 @@ function CommandProvider({
55372
55777
  obfuscation,
55373
55778
  toast
55374
55779
  ]);
55375
- const refreshSkills = useCallback6(async () => {
55780
+ const refreshSkills = useCallback8(async () => {
55376
55781
  await registry.refresh();
55377
55782
  setRegistryVersion((v) => v + 1);
55378
55783
  }, [registry]);
@@ -55388,7 +55793,7 @@ function CommandProvider({
55388
55793
  }
55389
55794
  return router2;
55390
55795
  }, []);
55391
- const resolveSkillContent = useCallback6((input) => {
55796
+ const resolveSkillContent = useCallback8((input) => {
55392
55797
  const trimmed = input.trim().replace(/^\/+/, "").toLowerCase();
55393
55798
  const entry = registry.get(trimmed);
55394
55799
  if (entry) {
@@ -55458,7 +55863,7 @@ function CommandProvider({
55458
55863
  }
55459
55864
  return names;
55460
55865
  }, []);
55461
- const executeCommand = useCallback6(async (input) => {
55866
+ const executeCommand = useCallback8(async (input) => {
55462
55867
  return await router.execute(input, ctx3);
55463
55868
  }, [router, ctx3]);
55464
55869
  const value = useMemo8(() => ({
@@ -55493,7 +55898,7 @@ function CommandProvider({
55493
55898
  import { useKeyboard as useKeyboard3, useRenderer as useRenderer3 } from "@opentui/react";
55494
55899
  import {
55495
55900
  createContext as createContext9,
55496
- useCallback as useCallback7,
55901
+ useCallback as useCallback9,
55497
55902
  useContext as useContext9,
55498
55903
  useRef as useRef4,
55499
55904
  useState as useState10
@@ -55578,7 +55983,7 @@ function DialogProvider({ children }) {
55578
55983
  const [externalDialogOpen, setExternalDialogOpen] = useState10(false);
55579
55984
  const renderer = useRenderer3();
55580
55985
  const focusRef = useRef4(null);
55581
- const refocus = useCallback7(() => {
55986
+ const refocus = useCallback9(() => {
55582
55987
  setTimeout(() => {
55583
55988
  const focus = focusRef.current;
55584
55989
  if (!focus)
@@ -55600,7 +56005,7 @@ function DialogProvider({ children }) {
55600
56005
  focus.focus();
55601
56006
  }, 1);
55602
56007
  }, [renderer]);
55603
- const clear = useCallback7(() => {
56008
+ const clear = useCallback9(() => {
55604
56009
  for (const item of stack) {
55605
56010
  if (item.onClose)
55606
56011
  item.onClose();
@@ -55609,7 +56014,7 @@ function DialogProvider({ children }) {
55609
56014
  setStack([]);
55610
56015
  refocus();
55611
56016
  }, [stack, refocus]);
55612
- const replace = useCallback7((element, options) => {
56017
+ const replace = useCallback9((element, options) => {
55613
56018
  if (stack.length === 0) {
55614
56019
  focusRef.current = renderer.currentFocusedRenderable;
55615
56020
  }
@@ -55673,7 +56078,7 @@ function useDialog() {
55673
56078
  // src/tui/context/focus.tsx
55674
56079
  import {
55675
56080
  createContext as createContext10,
55676
- useCallback as useCallback8,
56081
+ useCallback as useCallback10,
55677
56082
  useContext as useContext10,
55678
56083
  useRef as useRef5
55679
56084
  } from "react";
@@ -55681,17 +56086,17 @@ import { jsxDEV as jsxDEV12 } from "@opentui/react/jsx-dev-runtime";
55681
56086
  var FocusContext = createContext10(undefined);
55682
56087
  function FocusProvider({ children }) {
55683
56088
  const promptRef = useRef5(null);
55684
- const refocusPrompt = useCallback8(() => {
56089
+ const refocusPrompt = useCallback10(() => {
55685
56090
  setTimeout(() => {
55686
56091
  promptRef.current?.focus();
55687
56092
  }, 1);
55688
56093
  }, []);
55689
- const focusPrompt = useCallback8(() => promptRef.current?.focus(), []);
55690
- const blurPrompt = useCallback8(() => promptRef.current?.blur(), []);
55691
- const resetPrompt = useCallback8(() => promptRef.current?.reset(), []);
55692
- const setPromptValue = useCallback8((value) => promptRef.current?.setValue(value), []);
55693
- const getPromptValue = useCallback8(() => promptRef.current?.getValue() ?? "", []);
55694
- const registerPromptRef = useCallback8((ref) => {
56094
+ const focusPrompt = useCallback10(() => promptRef.current?.focus(), []);
56095
+ const blurPrompt = useCallback10(() => promptRef.current?.blur(), []);
56096
+ const resetPrompt = useCallback10(() => promptRef.current?.reset(), []);
56097
+ const setPromptValue = useCallback10((value) => promptRef.current?.setValue(value), []);
56098
+ const getPromptValue = useCallback10(() => promptRef.current?.getValue() ?? "", []);
56099
+ const registerPromptRef = useCallback10((ref) => {
55695
56100
  promptRef.current = ref;
55696
56101
  }, []);
55697
56102
  return /* @__PURE__ */ jsxDEV12(FocusContext.Provider, {
@@ -55709,17 +56114,17 @@ function FocusProvider({ children }) {
55709
56114
  }, undefined, false, undefined, this);
55710
56115
  }
55711
56116
  function useFocus() {
55712
- const context = useContext10(FocusContext);
55713
- if (!context) {
56117
+ const context2 = useContext10(FocusContext);
56118
+ if (!context2) {
55714
56119
  throw new Error("useFocus must be used within FocusProvider");
55715
56120
  }
55716
- return context;
56121
+ return context2;
55717
56122
  }
55718
56123
 
55719
56124
  // src/tui/context/input.tsx
55720
56125
  import {
55721
56126
  createContext as createContext11,
55722
- useCallback as useCallback9,
56127
+ useCallback as useCallback11,
55723
56128
  useContext as useContext11,
55724
56129
  useState as useState11
55725
56130
  } from "react";
@@ -55727,7 +56132,7 @@ import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
55727
56132
  var InputContext = createContext11(undefined);
55728
56133
  function InputProvider({ children }) {
55729
56134
  const [inputValue, setInputValue] = useState11("");
55730
- const clearInput = useCallback9(() => {
56135
+ const clearInput = useCallback11(() => {
55731
56136
  setInputValue("");
55732
56137
  }, []);
55733
56138
  return /* @__PURE__ */ jsxDEV13(InputContext.Provider, {
@@ -55741,11 +56146,11 @@ function InputProvider({ children }) {
55741
56146
  }, undefined, false, undefined, this);
55742
56147
  }
55743
56148
  function useInput() {
55744
- const context = useContext11(InputContext);
55745
- if (!context) {
56149
+ const context2 = useContext11(InputContext);
56150
+ if (!context2) {
55746
56151
  throw new Error("useInput must be used within InputProvider");
55747
56152
  }
55748
- return context;
56153
+ return context2;
55749
56154
  }
55750
56155
 
55751
56156
  // src/tui/components/shared/tool-registry.ts
@@ -57371,7 +57776,7 @@ function markdownToStyledText(rawContent, colors2) {
57371
57776
  import {
57372
57777
  SyntaxStyle
57373
57778
  } from "@opentui/core";
57374
- import { useCallback as useCallback10, useMemo as useMemo9, useRef as useRef6 } from "react";
57779
+ import { useCallback as useCallback12, useMemo as useMemo9, useRef as useRef6 } from "react";
57375
57780
  function useMarkdownSyntaxStyle() {
57376
57781
  const { colors: colors2 } = useTheme();
57377
57782
  return useMemo9(() => SyntaxStyle.fromStyles({
@@ -57392,7 +57797,7 @@ function useMarkdownSyntaxStyle() {
57392
57797
  }
57393
57798
  function useMarkdownRenderNode() {
57394
57799
  const { colors: colors2 } = useTheme();
57395
- return useCallback10((token, ctx3) => {
57800
+ return useCallback12((token, ctx3) => {
57396
57801
  if (token.type === "code") {
57397
57802
  const renderable = ctx3.defaultRender();
57398
57803
  if (renderable) {
@@ -58780,7 +59185,7 @@ function isToolMessage(msg) {
58780
59185
  }
58781
59186
 
58782
59187
  // src/tui/components/shared/tool-renderer.tsx
58783
- import { jsxDEV as jsxDEV19, Fragment } from "@opentui/react/jsx-dev-runtime";
59188
+ import { jsxDEV as jsxDEV19, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
58784
59189
  var TOOLS_WITH_LOG_WINDOW = new Set([
58785
59190
  "execute_command",
58786
59191
  "run_attack_surface",
@@ -58843,7 +59248,7 @@ var ToolRenderer = memo2(function ToolRenderer2({
58843
59248
  children: isPending ? /* @__PURE__ */ jsxDEV19(ShiningText, {
58844
59249
  text: summary,
58845
59250
  fg: colors2.warning
58846
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19(Fragment, {
59251
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19(Fragment2, {
58847
59252
  children: [
58848
59253
  /* @__PURE__ */ jsxDEV19("text", {
58849
59254
  fg: isError ? colors2.error : colors2.success,
@@ -59990,7 +60395,7 @@ var PromptInput = forwardRef(function PromptInput2({
59990
60395
  import { useKeyboard as useKeyboard5 } from "@opentui/react";
59991
60396
  import { useEffect as useEffect9, useRef as useRef9, useState as useState16 } from "react";
59992
60397
  import { jsxDEV as jsxDEV22 } from "@opentui/react/jsx-dev-runtime";
59993
- var PROVIDER_DISPLAY_NAMES = {
60398
+ var PROVIDER_DISPLAY_NAMES2 = {
59994
60399
  anthropic: "Anthropic",
59995
60400
  openai: "OpenAI",
59996
60401
  google: "Google",
@@ -60001,7 +60406,7 @@ var PROVIDER_DISPLAY_NAMES = {
60001
60406
  inception: "Inception",
60002
60407
  local: "Local"
60003
60408
  };
60004
- var providerDisplayName = (p) => PROVIDER_DISPLAY_NAMES[p] ?? p;
60409
+ var providerDisplayName = (p) => PROVIDER_DISPLAY_NAMES2[p] ?? p;
60005
60410
  function OperatorModeBar({
60006
60411
  operatorMode,
60007
60412
  modelName,
@@ -60582,7 +60987,7 @@ function HomeView({ onNavigate, onStartSession }) {
60582
60987
  useEffect11(() => {
60583
60988
  load().then(setCommandHistory);
60584
60989
  }, []);
60585
- const launchOperator = useCallback11((message, options) => {
60990
+ const launchOperator = useCallback13((message, options) => {
60586
60991
  const requireApproval = options?.requireApproval ?? true;
60587
60992
  route.navigate({
60588
60993
  type: "operator",
@@ -60593,10 +60998,10 @@ function HomeView({ onNavigate, onStartSession }) {
60593
60998
  }
60594
60999
  });
60595
61000
  }, [route]);
60596
- const pushHistory = useCallback11((entry) => {
61001
+ const pushHistory = useCallback13((entry) => {
60597
61002
  push(entry).then(() => setCommandHistory([...getEntries()]));
60598
61003
  }, []);
60599
- const handleSubmit = useCallback11((value) => {
61004
+ const handleSubmit = useCallback13((value) => {
60600
61005
  if (!value.trim())
60601
61006
  return;
60602
61007
  pushHistory(value.trim());
@@ -60608,7 +61013,7 @@ function HomeView({ onNavigate, onStartSession }) {
60608
61013
  const timer = setTimeout(() => setHintMessage(null), 3000);
60609
61014
  return () => clearTimeout(timer);
60610
61015
  }, [hintMessage]);
60611
- const handleCommandExecute = useCallback11(async (command) => {
61016
+ const handleCommandExecute = useCallback13(async (command) => {
60612
61017
  const trimmed = command.trim();
60613
61018
  pushHistory(trimmed);
60614
61019
  const handled = await executeCommand(command);
@@ -60805,7 +61210,7 @@ function ChatApp({
60805
61210
  });
60806
61211
  }
60807
61212
  }, [initialSessionId]);
60808
- const handleNavigate = useCallback12((view, options) => {
61213
+ const handleNavigate = useCallback14((view, options) => {
60809
61214
  if (view === "chat" && options?.sessionId) {
60810
61215
  sessions.get(options.sessionId).then((session) => {
60811
61216
  if (session) {
@@ -60817,7 +61222,7 @@ function ChatApp({
60817
61222
  setCurrentView(view);
60818
61223
  }
60819
61224
  }, []);
60820
- const handleStartSession = useCallback12(async (directive) => {
61225
+ const handleStartSession = useCallback14(async (directive) => {
60821
61226
  const session = await sessions.create({
60822
61227
  name: `exploration-${Date.now()}`,
60823
61228
  targets: []
@@ -60826,7 +61231,7 @@ function ChatApp({
60826
61231
  setInitialDirective(directive);
60827
61232
  setCurrentView("chat");
60828
61233
  }, []);
60829
- const handleConfigSubmit = useCallback12(async (configData) => {
61234
+ const handleConfigSubmit = useCallback14(async (configData) => {
60830
61235
  const hostname = parseHostFromTarget(configData.targetUrl);
60831
61236
  const timestamp = new Date().toISOString().split("T")[0];
60832
61237
  const sessionName = `${hostname}-${timestamp}`;
@@ -60849,12 +61254,12 @@ function ChatApp({
60849
61254
  setSessionModel(configData.model);
60850
61255
  setCurrentView("chat");
60851
61256
  }, []);
60852
- const handleExitChat = useCallback12(() => {
61257
+ const handleExitChat = useCallback14(() => {
60853
61258
  setCurrentView("home");
60854
61259
  setActiveSession(null);
60855
61260
  setInitialDirective(undefined);
60856
61261
  }, []);
60857
- const handleBackFromConfig = useCallback12(() => {
61262
+ const handleBackFromConfig = useCallback14(() => {
60858
61263
  setCurrentView("home");
60859
61264
  }, []);
60860
61265
  return /* @__PURE__ */ jsxDEV25("box", {
@@ -60887,7 +61292,7 @@ function parseHostFromTarget(target) {
60887
61292
 
60888
61293
  // src/tui/components/commands/advanced-settings.tsx
60889
61294
  import { useKeyboard as useKeyboard6 } from "@opentui/react";
60890
- import { useCallback as useCallback13, useState as useState20 } from "react";
61295
+ import { useCallback as useCallback15, useState as useState20 } from "react";
60891
61296
 
60892
61297
  // src/tui/components/dialog-layout.tsx
60893
61298
  import { jsxDEV as jsxDEV26 } from "@opentui/react/jsx-dev-runtime";
@@ -60959,7 +61364,7 @@ function AdvancedSettings({ onClose }) {
60959
61364
  const [saving, setSaving] = useState20(false);
60960
61365
  const [error, setError] = useState20(null);
60961
61366
  const [draftStrikeMode, setDraftStrikeMode] = useState20(config2.data.strikeMode ?? false);
60962
- const saveSettings = useCallback13(async () => {
61367
+ const saveSettings = useCallback15(async () => {
60963
61368
  if (saving)
60964
61369
  return;
60965
61370
  setSaving(true);
@@ -61041,9 +61446,9 @@ function AdvancedSettings({ onClose }) {
61041
61446
 
61042
61447
  // src/tui/components/commands/auth-flow.tsx
61043
61448
  import { useKeyboard as useKeyboard7 } from "@opentui/react";
61044
- import { useCallback as useCallback14, useEffect as useEffect13, useRef as useRef11, useState as useState21 } from "react";
61449
+ import { useCallback as useCallback16, useEffect as useEffect13, useRef as useRef11, useState as useState21 } from "react";
61045
61450
  init_auth();
61046
- init_config2();
61451
+ init_config();
61047
61452
 
61048
61453
  // src/tui/utils/open-url.ts
61049
61454
  var OPEN_FAILED_MSG = "Couldn't open browser — visit the URL shown instead";
@@ -61117,7 +61522,7 @@ function AuthFlow({ onClose, hideEsc }) {
61117
61522
  const goHome = () => {
61118
61523
  onClose();
61119
61524
  };
61120
- const cleanup = useCallback14(() => {
61525
+ const cleanup = useCallback16(() => {
61121
61526
  abortRef.current?.abort();
61122
61527
  abortRef.current = null;
61123
61528
  }, []);
@@ -61678,9 +62083,9 @@ function AuthFlow({ onClose, hideEsc }) {
61678
62083
 
61679
62084
  // src/tui/components/commands/credits-flow.tsx
61680
62085
  import { useKeyboard as useKeyboard8 } from "@opentui/react";
61681
- import { useCallback as useCallback15, useEffect as useEffect14, useState as useState22 } from "react";
62086
+ import { useCallback as useCallback17, useEffect as useEffect14, useState as useState22 } from "react";
61682
62087
  init_auth();
61683
- import { jsxDEV as jsxDEV29, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
62088
+ import { jsxDEV as jsxDEV29, Fragment as Fragment3 } from "@opentui/react/jsx-dev-runtime";
61684
62089
  function CreditsFlow({
61685
62090
  onClose,
61686
62091
  onOpenAuthDialog
@@ -61706,7 +62111,7 @@ function CreditsFlow({
61706
62111
  });
61707
62112
  setStep("browser-opened");
61708
62113
  };
61709
- const fetchBalance = useCallback15(async () => {
62114
+ const fetchBalance = useCallback17(async () => {
61710
62115
  setStep("loading");
61711
62116
  setError(null);
61712
62117
  try {
@@ -61816,7 +62221,7 @@ function CreditsFlow({
61816
62221
  error
61817
62222
  ]
61818
62223
  }, undefined, true, undefined, this)
61819
- }, undefined, false, undefined, this) : credits ? /* @__PURE__ */ jsxDEV29(Fragment2, {
62224
+ }, undefined, false, undefined, this) : credits ? /* @__PURE__ */ jsxDEV29(Fragment3, {
61820
62225
  children: [
61821
62226
  /* @__PURE__ */ jsxDEV29("box", {
61822
62227
  children: /* @__PURE__ */ jsxDEV29("text", {
@@ -62109,7 +62514,6 @@ function HelpDialog({ onClose }) {
62109
62514
  // src/tui/components/commands/models-display.tsx
62110
62515
  import { useKeyboard as useKeyboard10 } from "@opentui/react";
62111
62516
  // src/tui/components/model-picker/ModelPickerDialog.tsx
62112
- init_ai();
62113
62517
  import { jsxDEV as jsxDEV31 } from "@opentui/react/jsx-dev-runtime";
62114
62518
  function ModelPickerDialog({ onClose }) {
62115
62519
  const { colors: colors2 } = useTheme();
@@ -62123,17 +62527,9 @@ function ModelPickerDialog({ onClose }) {
62123
62527
  openAIReasoningEffort,
62124
62528
  setOpenAIReasoningEffort
62125
62529
  } = useAgent();
62126
- const thinkingSupported = modelSupportsThinking(model.id);
62127
- const openAIReasoningSupported = modelSupportsOpenAIReasoning(model.id);
62128
62530
  const footerActions = [
62129
62531
  { key: "Enter", label: "confirm", variant: "primary" }
62130
62532
  ];
62131
- if (thinkingSupported) {
62132
- footerActions.push({ key: "Space", label: "Extended Thinking" });
62133
- }
62134
- if (openAIReasoningSupported) {
62135
- footerActions.push({ key: "Space", label: "Reasoning Effort" });
62136
- }
62137
62533
  const title = /* @__PURE__ */ jsxDEV31("text", {
62138
62534
  children: [
62139
62535
  /* @__PURE__ */ jsxDEV31("span", {
@@ -62609,7 +63005,7 @@ function HITLWizard(props) {
62609
63005
  }
62610
63006
 
62611
63007
  // src/tui/components/commands/provider-manager.tsx
62612
- init_config2();
63008
+ init_config();
62613
63009
  import { useKeyboard as useKeyboard14 } from "@opentui/react";
62614
63010
  import { useState as useState27 } from "react";
62615
63011
 
@@ -62863,7 +63259,7 @@ function ProviderSelection({
62863
63259
  }
62864
63260
 
62865
63261
  // src/tui/components/commands/provider-manager.tsx
62866
- import { jsxDEV as jsxDEV37, Fragment as Fragment3 } from "@opentui/react/jsx-dev-runtime";
63262
+ import { jsxDEV as jsxDEV37, Fragment as Fragment4 } from "@opentui/react/jsx-dev-runtime";
62867
63263
  function ProviderManager({
62868
63264
  onClose,
62869
63265
  onOpenModelDialog
@@ -62939,7 +63335,7 @@ function ProviderManager({
62939
63335
  };
62940
63336
  const otherProviders = AVAILABLE_PROVIDERS.filter((p) => p.id !== "pensar");
62941
63337
  const selectedProviderInfo = AVAILABLE_PROVIDERS.find((p) => p.id === selectedProvider);
62942
- return /* @__PURE__ */ jsxDEV37(Fragment3, {
63338
+ return /* @__PURE__ */ jsxDEV37(Fragment4, {
62943
63339
  children: [
62944
63340
  flowState === "choosing" && /* @__PURE__ */ jsxDEV37(OnboardingChoice, {
62945
63341
  onPensarSelected: () => setFlowState("auth"),
@@ -63052,13 +63448,13 @@ function OnboardingChoice({
63052
63448
 
63053
63449
  // src/tui/components/commands/sessions-display.tsx
63054
63450
  import { useKeyboard as useKeyboard16 } from "@opentui/react";
63055
- import { useCallback as useCallback17, useEffect as useEffect18, useRef as useRef14, useState as useState29 } from "react";
63451
+ import { useCallback as useCallback19, useEffect as useEffect18, useRef as useRef14, useState as useState29 } from "react";
63056
63452
  init_session();
63057
63453
 
63058
63454
  // src/tui/hooks/use-sessions-list.ts
63059
63455
  import { existsSync, readdirSync } from "node:fs";
63060
63456
  import { join } from "node:path";
63061
- import { useCallback as useCallback16, useEffect as useEffect17, useState as useState28 } from "react";
63457
+ import { useCallback as useCallback18, useEffect as useEffect17, useState as useState28 } from "react";
63062
63458
  init_session();
63063
63459
  function countFindings(findingsPath) {
63064
63460
  try {
@@ -63076,7 +63472,7 @@ function useSessionsList() {
63076
63472
  const [allSessions, setAllSessions] = useState28([]);
63077
63473
  const [loading, setLoading] = useState28(true);
63078
63474
  const [searchTerm, setSearchTerm] = useState28("");
63079
- const loadSessions = useCallback16(async () => {
63475
+ const loadSessions = useCallback18(async () => {
63080
63476
  try {
63081
63477
  const enriched = [];
63082
63478
  for await (const session of list()) {
@@ -63101,7 +63497,7 @@ function useSessionsList() {
63101
63497
  useEffect17(() => {
63102
63498
  loadSessions();
63103
63499
  }, [loadSessions]);
63104
- const deleteSession = useCallback16(async (id) => {
63500
+ const deleteSession = useCallback18(async (id) => {
63105
63501
  setAllSessions((prev) => prev.filter((s) => s.id !== id));
63106
63502
  await sessions.remove({ sessionId: id });
63107
63503
  await loadSessions();
@@ -63334,7 +63730,7 @@ function SessionsDisplay({ onClose }) {
63334
63730
  initialOverflowRef.current = estimatedRows > availableListHeight;
63335
63731
  }
63336
63732
  const listHeight = initialOverflowRef.current ? availableListHeight : undefined;
63337
- const viewReport = useCallback17(async (sessionId) => {
63733
+ const viewReport = useCallback19(async (sessionId) => {
63338
63734
  const session = await sessions.get(sessionId);
63339
63735
  const content = readSessionReport(session.rootPath);
63340
63736
  if (!content) {
@@ -63345,7 +63741,7 @@ function SessionsDisplay({ onClose }) {
63345
63741
  setReportSessionPath(session.rootPath);
63346
63742
  setShowReportViewer(true);
63347
63743
  }, [toast]);
63348
- const openReportExternal = useCallback17(async () => {
63744
+ const openReportExternal = useCallback19(async () => {
63349
63745
  if (!reportSessionPath)
63350
63746
  return;
63351
63747
  const err = await openSessionReport(reportSessionPath);
@@ -63971,9 +64367,9 @@ function SkillsDialog({
63971
64367
  }
63972
64368
 
63973
64369
  // src/tui/components/commands/theme-picker.tsx
63974
- init_config2();
64370
+ init_config();
63975
64371
  import { useKeyboard as useKeyboard19 } from "@opentui/react";
63976
- import { useCallback as useCallback18, useRef as useRef16, useState as useState31 } from "react";
64372
+ import { useCallback as useCallback20, useRef as useRef16, useState as useState31 } from "react";
63977
64373
  import { jsxDEV as jsxDEV42 } from "@opentui/react/jsx-dev-runtime";
63978
64374
  function ThemePicker({ onClose }) {
63979
64375
  const dimensions = useDimensions();
@@ -63992,14 +64388,14 @@ function ThemePicker({ onClose }) {
63992
64388
  const originalThemeRef = useRef16(theme.name);
63993
64389
  const originalModeRef = useRef16(mode);
63994
64390
  const originalTransparentRef = useRef16(transparent);
63995
- const handleClose = useCallback18(() => {
64391
+ const handleClose = useCallback20(() => {
63996
64392
  setTheme(originalThemeRef.current);
63997
64393
  setMode(originalModeRef.current);
63998
64394
  if (transparent !== originalTransparentRef.current)
63999
64395
  toggleTransparent();
64000
64396
  onClose();
64001
64397
  }, [setTheme, setMode, transparent, toggleTransparent, onClose]);
64002
- const handleConfirm = useCallback18(async () => {
64398
+ const handleConfirm = useCallback20(async () => {
64003
64399
  const currentThemeName = availableThemes[selectedIndex];
64004
64400
  if (currentThemeName) {
64005
64401
  await config.update({ theme: currentThemeName });
@@ -64125,7 +64521,7 @@ function ThemePicker({ onClose }) {
64125
64521
  // src/tui/components/commands/web-wizard.tsx
64126
64522
  import { useKeyboard as useKeyboard20 } from "@opentui/react";
64127
64523
  import { useEffect as useEffect20, useRef as useRef17, useState as useState32 } from "react";
64128
- import { jsxDEV as jsxDEV43, Fragment as Fragment4 } from "@opentui/react/jsx-dev-runtime";
64524
+ import { jsxDEV as jsxDEV43, Fragment as Fragment5 } from "@opentui/react/jsx-dev-runtime";
64129
64525
  function capitalize(s) {
64130
64526
  return s.charAt(0).toUpperCase() + s.slice(1);
64131
64527
  }
@@ -64611,7 +65007,7 @@ function WebWizard({
64611
65007
  }, undefined, false, undefined, this)
64612
65008
  ]
64613
65009
  }, undefined, true, undefined, this),
64614
- advancedExpanded && /* @__PURE__ */ jsxDEV43(Fragment4, {
65010
+ advancedExpanded && /* @__PURE__ */ jsxDEV43(Fragment5, {
64615
65011
  children: [
64616
65012
  /* @__PURE__ */ jsxDEV43("box", {
64617
65013
  id: `field-${promptFieldIndex}`,
@@ -64849,7 +65245,7 @@ function WebWizard({
64849
65245
  }, undefined, false, undefined, this)
64850
65246
  ]
64851
65247
  }, undefined, true, undefined, this),
64852
- state.headers.mode === "custom" && /* @__PURE__ */ jsxDEV43(Fragment4, {
65248
+ state.headers.mode === "custom" && /* @__PURE__ */ jsxDEV43(Fragment5, {
64853
65249
  children: [
64854
65250
  /* @__PURE__ */ jsxDEV43("box", {
64855
65251
  id: `field-${headersNameIndex}`,
@@ -64897,7 +65293,7 @@ function WebWizard({
64897
65293
 
64898
65294
  // src/tui/components/error-boundary.tsx
64899
65295
  init_logger();
64900
- import React, { useCallback as useCallback19 } from "react";
65296
+ import React, { useCallback as useCallback21 } from "react";
64901
65297
  var MAX_ERRORS = 3;
64902
65298
  var ERROR_WINDOW_MS = 5000;
64903
65299
 
@@ -64932,7 +65328,7 @@ class ErrorBoundaryInner extends React.Component {
64932
65328
  }
64933
65329
  function ErrorBoundary({ children }) {
64934
65330
  const { toast } = useToast();
64935
- const handleError = useCallback19((message) => {
65331
+ const handleError = useCallback21((message) => {
64936
65332
  toast(message, "error");
64937
65333
  }, [toast]);
64938
65334
  return React.createElement(ErrorBoundaryInner, { onError: handleError }, children);
@@ -64986,19 +65382,52 @@ var useSession = () => {
64986
65382
  return session;
64987
65383
  };
64988
65384
 
64989
- // src/tui/components/footer.tsx
64990
- import { jsxDEV as jsxDEV45, Fragment as Fragment5 } from "@opentui/react/jsx-dev-runtime";
65385
+ // src/tui/components/usage-labels.ts
65386
+ var WIDE_MIN_WIDTH = 86;
65387
+ var NARROW_MIN_WIDTH = 56;
64991
65388
  function formatTokenCount(count) {
64992
65389
  if (count >= 1e6) {
64993
65390
  const v2 = count / 1e6;
64994
- return `${Number.isInteger(v2) ? v2.toFixed(0) : v2.toFixed(1)}M`;
65391
+ return `${Number.isInteger(v2) ? v2.toFixed(0) : v2.toFixed(1)}m`;
64995
65392
  }
64996
65393
  if (count >= 1000) {
64997
65394
  const v2 = count / 1000;
64998
- return `${Number.isInteger(v2) ? v2.toFixed(0) : v2.toFixed(1)}K`;
65395
+ return `${Number.isInteger(v2) ? v2.toFixed(0) : v2.toFixed(1)}k`;
64999
65396
  }
65000
65397
  return count.toString();
65001
65398
  }
65399
+ function buildUsageFooterLabels(input) {
65400
+ const { tokenUsage, contextUsage, width } = input;
65401
+ if (width < NARROW_MIN_WIDTH) {
65402
+ return { tokensLabel: null, contextLabel: null };
65403
+ }
65404
+ if (width >= WIDE_MIN_WIDTH) {
65405
+ let tokensLabel = `in ${formatTokenCount(tokenUsage.inputTokens)} out ${formatTokenCount(tokenUsage.outputTokens)}`;
65406
+ if (tokenUsage.cacheReadTokens > 0) {
65407
+ tokensLabel += ` cached ${formatTokenCount(tokenUsage.cacheReadTokens)}`;
65408
+ }
65409
+ return {
65410
+ tokensLabel,
65411
+ contextLabel: contextLabel(contextUsage, true)
65412
+ };
65413
+ }
65414
+ const total = tokenUsage.inputTokens + tokenUsage.outputTokens;
65415
+ return {
65416
+ tokensLabel: `${formatTokenCount(total)} tokens`,
65417
+ contextLabel: contextLabel(contextUsage, false)
65418
+ };
65419
+ }
65420
+ function contextLabel(context2, wide) {
65421
+ const percentage = contextPercentage(context2);
65422
+ if (percentage === null || !context2)
65423
+ return null;
65424
+ const pct = Math.floor(percentage);
65425
+ const limit = formatTokenCount(context2.contextLimit);
65426
+ return wide ? `context ${pct}% / ${limit}` : `${pct}% / ${limit}`;
65427
+ }
65428
+
65429
+ // src/tui/components/footer.tsx
65430
+ import { jsxDEV as jsxDEV45, Fragment as Fragment6 } from "@opentui/react/jsx-dev-runtime";
65002
65431
  function Footer({
65003
65432
  cwd = process.cwd(),
65004
65433
  showExitWarning = false
@@ -65077,126 +65506,54 @@ function Footer({
65077
65506
  }
65078
65507
  function AgentStatus() {
65079
65508
  const { colors: colors2 } = useTheme();
65080
- const { tokenUsage } = useAgent();
65509
+ const { usageStore } = useAgent();
65081
65510
  const { width: termWidth } = useDimensions();
65082
- const showTokenCount = termWidth > 85;
65083
- const tokenLabel = `↓${formatTokenCount(tokenUsage.inputTokens)} ↑${formatTokenCount(tokenUsage.outputTokens)}${tokenUsage.cachedTokens > 0 ? ` ⚡${formatTokenCount(tokenUsage.cachedTokens)}` : ""} Σ${formatTokenCount(tokenUsage.totalTokens)}`;
65511
+ const usage = useActiveSessionUsage(usageStore);
65512
+ const { tokensLabel, contextLabel: contextLabel2 } = buildUsageFooterLabels({
65513
+ tokenUsage: usage.tokenUsage,
65514
+ contextUsage: usage.contextUsage,
65515
+ width: termWidth
65516
+ });
65517
+ if (!tokensLabel)
65518
+ return null;
65084
65519
  return /* @__PURE__ */ jsxDEV45("box", {
65085
65520
  flexDirection: "row",
65086
65521
  gap: 1,
65087
65522
  children: [
65088
- showTokenCount && /* @__PURE__ */ jsxDEV45(Fragment5, {
65523
+ /* @__PURE__ */ jsxDEV45("text", {
65524
+ fg: colors2.text,
65525
+ children: tokensLabel
65526
+ }, undefined, false, undefined, this),
65527
+ contextLabel2 && /* @__PURE__ */ jsxDEV45(Fragment6, {
65089
65528
  children: [
65090
- /* @__PURE__ */ jsxDEV45("box", {
65091
- border: ["right"],
65092
- borderColor: colors2.primary
65529
+ /* @__PURE__ */ jsxDEV45("text", {
65530
+ fg: colors2.textMuted,
65531
+ children: "|"
65093
65532
  }, undefined, false, undefined, this),
65094
65533
  /* @__PURE__ */ jsxDEV45("text", {
65095
65534
  fg: colors2.text,
65096
- children: tokenLabel
65097
- }, undefined, false, undefined, this),
65098
- /* @__PURE__ */ jsxDEV45("box", {
65099
- border: ["right"],
65100
- borderColor: colors2.primary
65535
+ children: contextLabel2
65101
65536
  }, undefined, false, undefined, this)
65102
65537
  ]
65103
- }, undefined, true, undefined, this),
65104
- /* @__PURE__ */ jsxDEV45(ContextProgress, {
65105
- width: 16,
65106
- showPercent: true
65107
- }, undefined, false, undefined, this)
65538
+ }, undefined, true, undefined, this)
65108
65539
  ]
65109
65540
  }, undefined, true, undefined, this);
65110
65541
  }
65111
- var FILL_CHAR = "▬";
65112
- var DASH_CHAR = "─";
65113
- function ContextProgress({
65114
- width = 20,
65115
- showPercent = false
65116
- }) {
65117
- const { colors: colors2 } = useTheme();
65118
- const { model, tokenUsage } = useAgent();
65119
- const contextLength = model.contextLength ?? 200000;
65120
- const fraction = Math.max(0, Math.min(1, tokenUsage.totalTokens / contextLength));
65121
- const inner = Math.max(1, width - 2);
65122
- const filledCells = Math.round(fraction * inner);
65123
- const fillColor = colors2.success;
65124
- const sizeText = formatTokenCount(contextLength);
65125
- const labelText = showPercent ? `${Math.floor(fraction * 100)}% ${sizeText}` : sizeText;
65126
- const labelLen = labelText.length;
65127
- const labelStart = Math.max(0, inner - labelLen - 1);
65128
- const labelEnd = labelStart + labelLen;
65129
- const dashColor = withAlpha2(colors2.textMuted, 0.35);
65130
- const bracketColor = withAlpha2(colors2.textMuted, 0.7);
65131
- const cells = [];
65132
- cells.push({ ch: "[", fg: bracketColor });
65133
- for (let col = 0;col < inner; col++) {
65134
- const inLabel = col >= labelStart && col < labelEnd;
65135
- const isFilled = col < filledCells;
65136
- if (inLabel) {
65137
- cells.push({
65138
- ch: labelText[col - labelStart],
65139
- fg: colors2.text
65140
- });
65141
- } else if (isFilled) {
65142
- cells.push({ ch: FILL_CHAR, fg: fillColor });
65143
- } else {
65144
- cells.push({ ch: DASH_CHAR, fg: dashColor });
65145
- }
65146
- }
65147
- cells.push({ ch: "]", fg: bracketColor });
65148
- return /* @__PURE__ */ jsxDEV45("box", {
65149
- flexDirection: "row",
65150
- children: cells.map((cell, i) => /* @__PURE__ */ jsxDEV45("text", {
65151
- fg: cell.fg,
65152
- content: cell.ch
65153
- }, i, false, undefined, this))
65154
- }, undefined, false, undefined, this);
65155
- }
65156
65542
 
65157
65543
  // src/tui/components/operator-dashboard/index.tsx
65158
65544
  init_dist();
65159
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
65160
- import { isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
65545
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
65546
+ import { isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
65161
65547
  import { useKeyboard as useKeyboard26 } from "@opentui/react";
65162
65548
  import {
65163
- useCallback as useCallback20,
65549
+ useCallback as useCallback22,
65164
65550
  useEffect as useEffect25,
65165
65551
  useMemo as useMemo22,
65166
65552
  useRef as useRef21,
65167
65553
  useState as useState39,
65168
- useSyncExternalStore as useSyncExternalStore2
65554
+ useSyncExternalStore as useSyncExternalStore3
65169
65555
  } from "react";
65170
65556
  init_ai();
65171
-
65172
- // src/core/integrations/wandb/upload.ts
65173
- async function createWandbUploader(session, opts) {
65174
- const resolved = resolveConfig(opts);
65175
- if (!resolved.available)
65176
- return null;
65177
- const tracer = await createWeaveTracer(resolved.config);
65178
- if (!tracer)
65179
- return null;
65180
- return {
65181
- onRecord: (record) => tracer.logRecord(record, session.id),
65182
- finalize: () => tracer.finish()
65183
- };
65184
- }
65185
- async function attachWandbToEventBus(session, eventBus, opts) {
65186
- const uploader = await createWandbUploader(session, opts);
65187
- if (!uploader)
65188
- return null;
65189
- const handler = (e) => {
65190
- uploader.onRecord(e.record);
65191
- };
65192
- eventBus.on("trace-record", handler);
65193
- return async () => {
65194
- eventBus.off("trace-record", handler);
65195
- await uploader.finalize();
65196
- };
65197
- }
65198
-
65199
- // src/tui/components/operator-dashboard/index.tsx
65200
65557
  init_session();
65201
65558
 
65202
65559
  // src/tui/components/chat/approval-inline.tsx
@@ -66464,6 +66821,202 @@ function ScreenshotModal({ screenshots, initialIndex, onClose }) {
66464
66821
  }, undefined, true, undefined, this);
66465
66822
  }
66466
66823
 
66824
+ // src/tui/components/operator-dashboard/conversation.ts
66825
+ init_session();
66826
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
66827
+ import { join as join3 } from "node:path";
66828
+ function recoverAbortedConversation(conversation, partialText, displayMessages) {
66829
+ if (conversation.at(-1)?.role !== "user")
66830
+ return null;
66831
+ const pendingTools = displayMessages.filter((message) => message.role === "tool" && typeof message.toolCallId === "string" && typeof message.toolName === "string" && (message.status === "pending" || message.status === "streaming"));
66832
+ const assistantContent = [
66833
+ {
66834
+ type: "text",
66835
+ text: partialText.trim() || "[Response interrupted by user.]"
66836
+ },
66837
+ ...pendingTools.map((tool) => ({
66838
+ type: "tool-call",
66839
+ toolCallId: tool.toolCallId,
66840
+ toolName: tool.toolName,
66841
+ input: tool.args ?? {}
66842
+ }))
66843
+ ];
66844
+ const recovered = [
66845
+ ...conversation,
66846
+ {
66847
+ role: "assistant",
66848
+ content: assistantContent
66849
+ }
66850
+ ];
66851
+ if (pendingTools.length === 0)
66852
+ return recovered;
66853
+ return [
66854
+ ...recovered,
66855
+ {
66856
+ role: "tool",
66857
+ content: pendingTools.map((tool) => ({
66858
+ type: "tool-result",
66859
+ toolCallId: tool.toolCallId,
66860
+ toolName: tool.toolName ?? "unknown",
66861
+ output: {
66862
+ type: "text",
66863
+ value: "Cancelled by user."
66864
+ }
66865
+ }))
66866
+ }
66867
+ ];
66868
+ }
66869
+ function rewriteToolResultOutput(conversation, toolCallId, value) {
66870
+ const wrappedOutput = {
66871
+ type: "json",
66872
+ value
66873
+ };
66874
+ let changed = false;
66875
+ const next = conversation.map((msg) => {
66876
+ if (msg.role !== "tool")
66877
+ return msg;
66878
+ if (!Array.isArray(msg.content))
66879
+ return msg;
66880
+ let mutated = false;
66881
+ const nextContent = msg.content.map((part) => {
66882
+ if (part && typeof part === "object" && "type" in part && part.type === "tool-result" && part.toolCallId === toolCallId) {
66883
+ mutated = true;
66884
+ return {
66885
+ ...part,
66886
+ output: wrappedOutput
66887
+ };
66888
+ }
66889
+ return part;
66890
+ });
66891
+ if (!mutated)
66892
+ return msg;
66893
+ changed = true;
66894
+ return { ...msg, content: nextContent };
66895
+ });
66896
+ return changed ? next : conversation;
66897
+ }
66898
+ function recoverAbortedTranscript(input) {
66899
+ const messagesPath = join3(input.rootPath, "messages.json");
66900
+ let conversation = input.conversation;
66901
+ let recovered = null;
66902
+ try {
66903
+ if (existsSync3(messagesPath)) {
66904
+ const raw = JSON.parse(readFileSync3(messagesPath, "utf-8"));
66905
+ if (Array.isArray(raw) && raw.length > 0) {
66906
+ conversation = normalizeMessages(getResumeMessages(raw));
66907
+ }
66908
+ }
66909
+ recovered = recoverAbortedConversation(conversation, input.partialText, input.displayMessages);
66910
+ } catch {}
66911
+ if (!recovered)
66912
+ return conversation;
66913
+ conversation = recovered;
66914
+ try {
66915
+ writeFileSync(messagesPath, JSON.stringify(recovered, null, 2));
66916
+ } catch {}
66917
+ return conversation;
66918
+ }
66919
+
66920
+ // src/tui/components/operator-dashboard/display-state.ts
66921
+ var MAX_LOG_LINES = 200;
66922
+ function appendStreamedText(messages, accumulated) {
66923
+ const last = messages[messages.length - 1];
66924
+ if (last && last.role === "assistant") {
66925
+ const updated = [...messages];
66926
+ updated[updated.length - 1] = { ...last, content: accumulated };
66927
+ return updated;
66928
+ }
66929
+ return [
66930
+ ...messages,
66931
+ { role: "assistant", content: accumulated, createdAt: new Date }
66932
+ ];
66933
+ }
66934
+ function startStreamingToolCall(messages, toolCallId, toolName) {
66935
+ return [
66936
+ ...messages,
66937
+ {
66938
+ role: "tool",
66939
+ content: "",
66940
+ createdAt: new Date,
66941
+ toolCallId,
66942
+ toolName,
66943
+ args: {},
66944
+ status: "streaming"
66945
+ }
66946
+ ];
66947
+ }
66948
+ function applyToolCallDelta(messages, toolCallId, parsed) {
66949
+ const contentText = extractStreamableContent(parsed);
66950
+ const logs = contentText ? contentText.split(`
66951
+ `) : undefined;
66952
+ const idx = messages.findIndex((m2) => isToolMessage(m2) && m2.toolCallId === toolCallId);
66953
+ if (idx === -1)
66954
+ return messages;
66955
+ const updated = [...messages];
66956
+ updated[idx] = { ...updated[idx], args: parsed, ...logs && { logs } };
66957
+ return updated;
66958
+ }
66959
+ function applyToolCall(messages, toolCallId, toolName, args) {
66960
+ const idx = messages.findIndex((m2) => isToolMessage(m2) && m2.toolCallId === toolCallId);
66961
+ if (idx !== -1) {
66962
+ const updated = [...messages];
66963
+ updated[idx] = {
66964
+ ...updated[idx],
66965
+ args,
66966
+ logs: undefined,
66967
+ status: "pending"
66968
+ };
66969
+ return updated;
66970
+ }
66971
+ return [
66972
+ ...messages,
66973
+ {
66974
+ role: "tool",
66975
+ content: "",
66976
+ createdAt: new Date,
66977
+ toolCallId,
66978
+ toolName,
66979
+ args,
66980
+ status: "pending"
66981
+ }
66982
+ ];
66983
+ }
66984
+ function applyToolResult(messages, toolCallId, result) {
66985
+ const idx = messages.findIndex((m2) => isToolMessage(m2) && m2.toolCallId === toolCallId);
66986
+ if (idx === -1)
66987
+ return messages;
66988
+ const updated = [...messages];
66989
+ updated[idx] = { ...updated[idx], status: "completed", result };
66990
+ return updated;
66991
+ }
66992
+ function mergeCommandOutput(messages, buf) {
66993
+ const idx = messages.findLastIndex((m2) => isToolMessage(m2) && (m2.status === "pending" || m2.status === "streaming"));
66994
+ if (idx === -1)
66995
+ return messages;
66996
+ const msg = messages[idx];
66997
+ const existing = msg.logs ?? [];
66998
+ const incoming = buf.split(`
66999
+ `);
67000
+ let merged;
67001
+ if (existing.length > 0 && !buf.startsWith(`
67002
+ `)) {
67003
+ merged = [...existing];
67004
+ merged[merged.length - 1] += incoming[0];
67005
+ merged.push(...incoming.slice(1));
67006
+ } else {
67007
+ merged = [...existing, ...incoming];
67008
+ }
67009
+ if (merged.length > MAX_LOG_LINES) {
67010
+ merged = merged.slice(-MAX_LOG_LINES);
67011
+ }
67012
+ const updated = [...messages];
67013
+ updated[idx] = { ...msg, logs: merged };
67014
+ return updated;
67015
+ }
67016
+ function markInFlightToolsErrored(messages, result) {
67017
+ return messages.map((m2) => isToolMessage(m2) && (m2.status === "pending" || m2.status === "streaming") ? { ...m2, status: "error", result } : m2);
67018
+ }
67019
+
66467
67020
  // src/tui/components/operator-dashboard/logic.ts
66468
67021
  var OPERATOR_ALLOWED_COMMANDS = new Set([
66469
67022
  "/models",
@@ -66741,17 +67294,6 @@ ${refinementBlock}`;
66741
67294
  function resolveInputFocused(status, dialogStackLength, externalDialogOpen) {
66742
67295
  return status !== "running" && dialogStackLength === 0 && !externalDialogOpen;
66743
67296
  }
66744
- function accumulateTokenUsage(current, stepInputTokens, stepOutputTokens) {
66745
- if (stepInputTokens <= 0 && stepOutputTokens <= 0)
66746
- return null;
66747
- return {
66748
- inputTokens: current.inputTokens + stepInputTokens,
66749
- outputTokens: current.outputTokens + stepOutputTokens,
66750
- totalTokens: current.totalTokens + stepInputTokens + stepOutputTokens,
66751
- cachedTokens: current.cachedTokens,
66752
- cacheWriteTokens: current.cacheWriteTokens
66753
- };
66754
- }
66755
67297
 
66756
67298
  // src/tui/components/operator-dashboard/queue.ts
66757
67299
  var nextId3 = 0;
@@ -66841,6 +67383,401 @@ function QueuedMessages({
66841
67383
  }, undefined, true, undefined, this);
66842
67384
  }
66843
67385
 
67386
+ // src/tui/components/operator-dashboard/workflow-data.ts
67387
+ function isPentestAgent(id) {
67388
+ return id !== undefined && /^pentest-agent-\d+$/.test(id);
67389
+ }
67390
+ function initialWorkflowData() {
67391
+ return {
67392
+ currentPhase: "discovery",
67393
+ discovery: { label: "", status: "pending", logs: [] },
67394
+ pentesting: { label: "", status: "idle", subagents: {} },
67395
+ reporting: { label: "", status: "idle" }
67396
+ };
67397
+ }
67398
+ function updateWorkflowDataMessage(messages, updater) {
67399
+ const idx = messages.findLastIndex((m2) => isToolMessage(m2) && m2.toolName === "run_pentest_workflow" && (m2.status === "pending" || m2.status === "streaming"));
67400
+ if (idx === -1)
67401
+ return messages;
67402
+ const msg = messages[idx];
67403
+ const wd = msg.workflowData ?? initialWorkflowData();
67404
+ const updated = [...messages];
67405
+ updated[idx] = { ...msg, workflowData: updater(wd) };
67406
+ return updated;
67407
+ }
67408
+ function applyWorkflowSubagentSpawn(wd, subagentId, name) {
67409
+ return {
67410
+ ...wd,
67411
+ pentesting: {
67412
+ ...wd.pentesting,
67413
+ subagents: {
67414
+ ...wd.pentesting.subagents,
67415
+ [subagentId]: { name, status: "pending", logs: [] }
67416
+ }
67417
+ }
67418
+ };
67419
+ }
67420
+ function applyWorkflowSubagentComplete(wd, subagentId, status) {
67421
+ const entry = wd.pentesting.subagents[subagentId];
67422
+ if (!entry)
67423
+ return wd;
67424
+ return {
67425
+ ...wd,
67426
+ pentesting: {
67427
+ ...wd.pentesting,
67428
+ subagents: {
67429
+ ...wd.pentesting.subagents,
67430
+ [subagentId]: { ...entry, status }
67431
+ }
67432
+ }
67433
+ };
67434
+ }
67435
+ function applyWorkflowPhaseStart(wd, phase, label) {
67436
+ const next = { ...wd, currentPhase: phase };
67437
+ if (phase === "discovery") {
67438
+ next.discovery = { ...wd.discovery, label, status: "pending", logs: [] };
67439
+ } else if (phase === "pentesting") {
67440
+ next.pentesting = { ...wd.pentesting, label, status: "pending" };
67441
+ } else if (phase === "reporting") {
67442
+ next.reporting = { ...wd.reporting, label, status: "pending" };
67443
+ }
67444
+ return next;
67445
+ }
67446
+ function applyWorkflowPhaseComplete(wd, phase, summary) {
67447
+ const next = { ...wd };
67448
+ if (phase === "discovery") {
67449
+ const targets = summary.targets ?? [];
67450
+ next.discovery = {
67451
+ ...wd.discovery,
67452
+ status: "complete",
67453
+ targets,
67454
+ cached: summary.cached
67455
+ };
67456
+ } else if (phase === "pentesting") {
67457
+ next.pentesting = { ...wd.pentesting, status: "complete" };
67458
+ } else if (phase === "reporting") {
67459
+ next.reporting = {
67460
+ ...wd.reporting,
67461
+ status: "complete",
67462
+ findingsCount: summary.findingsCount,
67463
+ findingsBySeverity: summary.findingsBySeverity,
67464
+ reportPath: summary.reportPath
67465
+ };
67466
+ next.currentPhase = "complete";
67467
+ }
67468
+ return next;
67469
+ }
67470
+
67471
+ // src/tui/components/operator-dashboard/run-events.ts
67472
+ function bindOperatorRunEvents(bus, options) {
67473
+ const { isCurrent, handlers } = options;
67474
+ const offs = [];
67475
+ let boundError = false;
67476
+ const bind = (event, handler) => {
67477
+ if (!handler)
67478
+ return;
67479
+ if (event === "error")
67480
+ boundError = true;
67481
+ const wrapped = (e) => {
67482
+ if (!isCurrent())
67483
+ return;
67484
+ try {
67485
+ handler(e);
67486
+ } catch (err) {
67487
+ console.error(`[operator] ${event} handler failed:`, err);
67488
+ }
67489
+ };
67490
+ bus.on(event, wrapped);
67491
+ offs.push(() => bus.off(event, wrapped));
67492
+ };
67493
+ bind("text-delta", handlers.onTextDelta);
67494
+ bind("tool-call-start", handlers.onToolCallStart);
67495
+ bind("tool-call-delta", handlers.onToolCallDelta);
67496
+ bind("tool-call-complete", handlers.onToolCallComplete);
67497
+ bind("tool-result", handlers.onToolResult);
67498
+ bind("command-output", handlers.onCommandOutput);
67499
+ bind("error", handlers.onError);
67500
+ bind("subagent-spawn", handlers.onSubagentSpawn);
67501
+ bind("subagent-complete", handlers.onSubagentComplete);
67502
+ bind("workflow-phase-start", handlers.onWorkflowPhaseStart);
67503
+ bind("workflow-phase-complete", handlers.onWorkflowPhaseComplete);
67504
+ let unbound = false;
67505
+ return () => {
67506
+ if (unbound)
67507
+ return;
67508
+ unbound = true;
67509
+ for (const off of offs)
67510
+ off();
67511
+ offs.length = 0;
67512
+ if (boundError)
67513
+ bus.on("error", () => {});
67514
+ };
67515
+ }
67516
+ var COMMAND_OUTPUT_FLUSH_MS = 150;
67517
+ function createDisplayEventHandlers(sink) {
67518
+ let partialText = "";
67519
+ const toolArgsDeltas = new Map;
67520
+ let commandOutputBuf = "";
67521
+ let flushTimer = null;
67522
+ const flushCommandOutput = () => {
67523
+ if (!commandOutputBuf)
67524
+ return;
67525
+ const buf = commandOutputBuf;
67526
+ commandOutputBuf = "";
67527
+ sink.updateMessages((messages) => mergeCommandOutput(messages, buf));
67528
+ };
67529
+ const stopCommandOutputFlush = () => {
67530
+ if (flushTimer) {
67531
+ clearInterval(flushTimer);
67532
+ flushTimer = null;
67533
+ }
67534
+ };
67535
+ return {
67536
+ onTextDelta(e) {
67537
+ sink.setThinking(false);
67538
+ partialText += e.text;
67539
+ const accumulated = partialText;
67540
+ sink.updateMessages((messages) => appendStreamedText(messages, accumulated));
67541
+ },
67542
+ onToolCallStart(e) {
67543
+ sink.setThinking(false);
67544
+ partialText = "";
67545
+ toolArgsDeltas.set(e.toolCallId, { accumulated: "" });
67546
+ sink.updateMessages((messages) => startStreamingToolCall(messages, e.toolCallId, e.toolName));
67547
+ },
67548
+ onToolCallDelta(e) {
67549
+ const entry = toolArgsDeltas.get(e.toolCallId);
67550
+ const accumulated = (entry?.accumulated ?? "") + e.argsTextDelta;
67551
+ toolArgsDeltas.set(e.toolCallId, { accumulated });
67552
+ const parsed = tryParsePartialJson(accumulated);
67553
+ if (!parsed)
67554
+ return;
67555
+ sink.updateMessages((messages) => applyToolCallDelta(messages, e.toolCallId, parsed));
67556
+ },
67557
+ onToolCallComplete(e) {
67558
+ sink.setThinking(false);
67559
+ partialText = "";
67560
+ toolArgsDeltas.delete(e.toolCallId);
67561
+ const args = e.args && typeof e.args === "object" && !Array.isArray(e.args) && e.args !== null ? e.args : undefined;
67562
+ sink.updateMessages((messages) => applyToolCall(messages, e.toolCallId, e.toolName, args));
67563
+ },
67564
+ onToolResult(e) {
67565
+ flushCommandOutput();
67566
+ stopCommandOutputFlush();
67567
+ sink.setThinking(true);
67568
+ partialText = "";
67569
+ sink.updateMessages((messages) => applyToolResult(messages, e.toolCallId, e.result));
67570
+ },
67571
+ onCommandOutput(e) {
67572
+ commandOutputBuf += e.data;
67573
+ if (!flushTimer) {
67574
+ flushTimer = setInterval(flushCommandOutput, COMMAND_OUTPUT_FLUSH_MS);
67575
+ }
67576
+ },
67577
+ onError(e) {
67578
+ console.error("Agent error:", e.error);
67579
+ const errorMessage = e.error instanceof Error ? e.error.message : "Unknown error";
67580
+ sink.setError(errorMessage);
67581
+ sink.updateMessages((messages) => markInFlightToolsErrored(messages, errorMessage));
67582
+ },
67583
+ getPartialText() {
67584
+ return partialText;
67585
+ },
67586
+ resetPartialText() {
67587
+ partialText = "";
67588
+ },
67589
+ flushCommandOutput,
67590
+ stopCommandOutputFlush,
67591
+ dispose: stopCommandOutputFlush
67592
+ };
67593
+ }
67594
+ function toRecordArgs(args) {
67595
+ return args && typeof args === "object" && !Array.isArray(args) && args !== null ? args : undefined;
67596
+ }
67597
+ function createRunEventProjections(deps) {
67598
+ const { display, subagents, updateWorkflowData } = deps;
67599
+ return {
67600
+ handlers: {
67601
+ onTextDelta: (d2) => {
67602
+ if (d2.subagentId) {
67603
+ subagents.appendText(d2.subagentId, d2.text);
67604
+ return;
67605
+ }
67606
+ display.onTextDelta(d2);
67607
+ },
67608
+ onToolCallStart: (d2) => {
67609
+ if (d2.subagentId) {
67610
+ subagents.addStreamingToolCall(d2.subagentId, d2.toolCallId, d2.toolName);
67611
+ return;
67612
+ }
67613
+ display.onToolCallStart(d2);
67614
+ },
67615
+ onToolCallDelta: (d2) => {
67616
+ if (d2.subagentId) {
67617
+ subagents.appendToolCallDelta(d2.subagentId, d2.toolCallId, d2.argsTextDelta);
67618
+ return;
67619
+ }
67620
+ display.onToolCallDelta(d2);
67621
+ },
67622
+ onToolCallComplete: (d2) => {
67623
+ if (d2.subagentId) {
67624
+ subagents.addToolCall(d2.subagentId, d2.toolCallId, d2.toolName, toRecordArgs(d2.args) ?? {});
67625
+ return;
67626
+ }
67627
+ display.onToolCallComplete(d2);
67628
+ deps.onRootToolCallStarted?.();
67629
+ if (d2.toolName === ASK_USER_QUESTIONS_TOOL_NAME) {
67630
+ const rawQuestions = toRecordArgs(d2.args)?.questions;
67631
+ if (Array.isArray(rawQuestions) && rawQuestions.length > 0) {
67632
+ deps.questions.onAsked(d2.toolCallId, rawQuestions);
67633
+ }
67634
+ }
67635
+ },
67636
+ onToolResult: (d2) => {
67637
+ if (d2.subagentId) {
67638
+ subagents.updateToolResult(d2.subagentId, d2.toolCallId, d2.result);
67639
+ return;
67640
+ }
67641
+ display.onToolResult(d2);
67642
+ if (d2.toolName === "submit_plan" && d2.result?.success === true) {
67643
+ deps.onPlanSubmitted?.();
67644
+ }
67645
+ },
67646
+ onCommandOutput: (d2) => {
67647
+ if (d2.subagentId)
67648
+ return;
67649
+ display.onCommandOutput(d2);
67650
+ },
67651
+ onError: (d2) => {
67652
+ if (d2.subagentId) {
67653
+ const errMsg = d2.error instanceof Error ? d2.error.message : "Unknown error";
67654
+ subagents.appendText(d2.subagentId, `
67655
+ Error: ${errMsg}
67656
+ `);
67657
+ return;
67658
+ }
67659
+ display.onError(d2);
67660
+ deps.questions.onCleared();
67661
+ },
67662
+ onSubagentSpawn: ({ subagentId, name }) => {
67663
+ if (subagentId.startsWith("app:"))
67664
+ return;
67665
+ subagents.spawnSession(subagentId, name);
67666
+ if (!isPentestAgent(subagentId))
67667
+ return;
67668
+ updateWorkflowData((wd) => applyWorkflowSubagentSpawn(wd, subagentId, name));
67669
+ },
67670
+ onSubagentComplete: ({ subagentId, status }) => {
67671
+ if (subagentId.startsWith("app:"))
67672
+ return;
67673
+ subagents.completeSession(subagentId, status);
67674
+ if (!isPentestAgent(subagentId))
67675
+ return;
67676
+ updateWorkflowData((wd) => applyWorkflowSubagentComplete(wd, subagentId, status));
67677
+ },
67678
+ onWorkflowPhaseStart: (d2) => {
67679
+ display.resetPartialText();
67680
+ if (d2.phase === "discovery") {
67681
+ deps.clearSubagentSessions();
67682
+ }
67683
+ updateWorkflowData((wd) => applyWorkflowPhaseStart(wd, d2.phase, d2.label));
67684
+ },
67685
+ onWorkflowPhaseComplete: (d2) => {
67686
+ display.resetPartialText();
67687
+ updateWorkflowData((wd) => applyWorkflowPhaseComplete(wd, d2.phase, d2.summary));
67688
+ }
67689
+ }
67690
+ };
67691
+ }
67692
+
67693
+ // src/core/integrations/wandb/upload.ts
67694
+ async function createWandbUploader(session, opts) {
67695
+ const resolved = resolveConfig(opts);
67696
+ if (!resolved.available)
67697
+ return null;
67698
+ const tracer = await createWeaveTracer(resolved.config);
67699
+ if (!tracer)
67700
+ return null;
67701
+ return {
67702
+ onRecord: (record) => tracer.logRecord(record, session.id),
67703
+ finalize: () => tracer.finish()
67704
+ };
67705
+ }
67706
+ async function attachWandbToEventBus(session, eventBus, opts) {
67707
+ const uploader = await createWandbUploader(session, opts);
67708
+ if (!uploader)
67709
+ return null;
67710
+ const handler = (e) => {
67711
+ uploader.onRecord(e.record);
67712
+ };
67713
+ eventBus.on("trace-record", handler);
67714
+ return async () => {
67715
+ eventBus.off("trace-record", handler);
67716
+ await uploader.finalize();
67717
+ };
67718
+ }
67719
+
67720
+ // src/tui/components/operator-dashboard/run-tracing.ts
67721
+ class RunTraceSession {
67722
+ eventBus;
67723
+ options;
67724
+ cleanup = null;
67725
+ earlyBuffer = [];
67726
+ countedSubagentSteps = new Set;
67727
+ earlyBufferHandler = (e) => {
67728
+ this.earlyBuffer?.push(e);
67729
+ };
67730
+ constructor(eventBus, options) {
67731
+ this.eventBus = eventBus;
67732
+ this.options = options;
67733
+ eventBus.on("trace-record", this.earlyBufferHandler);
67734
+ eventBus.on("trace-record", this.countSubagentStep);
67735
+ }
67736
+ async tryAttach(session) {
67737
+ if (this.cleanup)
67738
+ return;
67739
+ const attach = this.options.attach ?? attachWandbToEventBus;
67740
+ const cleanup = await attach(session, this.eventBus).catch((e) => {
67741
+ console.error("[wandb] Attach failed:", e);
67742
+ return null;
67743
+ });
67744
+ const buffered = this.earlyBuffer;
67745
+ this.stopBuffering();
67746
+ if (cleanup) {
67747
+ this.cleanup = cleanup;
67748
+ if (buffered) {
67749
+ for (const e of buffered)
67750
+ this.eventBus.emit("trace-record", e);
67751
+ }
67752
+ }
67753
+ }
67754
+ async cleanupRun() {
67755
+ this.stopBuffering();
67756
+ this.eventBus.off("trace-record", this.countSubagentStep);
67757
+ if (this.cleanup) {
67758
+ const fn = this.cleanup;
67759
+ this.cleanup = null;
67760
+ await fn().catch((e) => console.error("[wandb] Flush failed:", e));
67761
+ }
67762
+ }
67763
+ stopBuffering() {
67764
+ this.earlyBuffer = null;
67765
+ this.eventBus.off("trace-record", this.earlyBufferHandler);
67766
+ }
67767
+ countSubagentStep = (e) => {
67768
+ if (!this.options.isCurrent())
67769
+ return;
67770
+ const { record, subagentId } = e;
67771
+ if (!subagentId || record.type !== "step")
67772
+ return;
67773
+ const key = `${subagentId}:${record.stepIndex}:${record.timestamp}`;
67774
+ if (this.countedSubagentSteps.has(key))
67775
+ return;
67776
+ this.countedSubagentSteps.add(key);
67777
+ this.options.recordTokenUsage(record.usage.inputTokens, record.usage.outputTokens, record.usage.cacheReadTokens ?? 0, record.usage.cacheWriteTokens ?? 0);
67778
+ };
67779
+ }
67780
+
66844
67781
  // src/tui/components/operator-dashboard/subagent-dialog.tsx
66845
67782
  import { useKeyboard as useKeyboard25 } from "@opentui/react";
66846
67783
  import {
@@ -66848,7 +67785,7 @@ import {
66848
67785
  useMemo as useMemo21,
66849
67786
  useRef as useRef20,
66850
67787
  useState as useState38,
66851
- useSyncExternalStore
67788
+ useSyncExternalStore as useSyncExternalStore2
66852
67789
  } from "react";
66853
67790
 
66854
67791
  // src/tui/components/operator-dashboard/subagent-detail-view.tsx
@@ -66930,6 +67867,22 @@ function createSubagentStore() {
66930
67867
  setState
66931
67868
  };
66932
67869
  }
67870
+ function markSubagentsInterrupted(prev) {
67871
+ let changed = false;
67872
+ const next = new Map(prev);
67873
+ for (const [id, session] of next) {
67874
+ const hasInFlight = session.messages.some((m2) => m2.role === "tool" && (m2.status === "pending" || m2.status === "streaming"));
67875
+ if (!hasInFlight && session.status !== "running")
67876
+ continue;
67877
+ changed = true;
67878
+ next.set(id, {
67879
+ ...session,
67880
+ status: session.status === "running" ? "cancelled" : session.status,
67881
+ messages: session.messages.map((m2) => m2.role === "tool" && (m2.status === "pending" || m2.status === "streaming") ? { ...m2, status: "error", result: "Interrupted" } : m2)
67882
+ });
67883
+ }
67884
+ return changed ? next : prev;
67885
+ }
66933
67886
  function createSubagentSessionHelpers(setState) {
66934
67887
  const toolArgsDeltaBuffers = new Map;
66935
67888
  const spawnSession = (id, name, input) => {
@@ -67340,7 +68293,7 @@ function SubagentDialog({ store }) {
67340
68293
  const [view, setView] = useState38({ type: "hub" });
67341
68294
  const viewRef = useRef20(view);
67342
68295
  viewRef.current = view;
67343
- const sessions2 = useSyncExternalStore(store.subscribe, store.getSnapshot);
68296
+ const sessions2 = useSyncExternalStore2(store.subscribe, store.getSnapshot);
67344
68297
  useEffect24(() => {
67345
68298
  setSize(view.type === "detail" ? "xlarge" : "large");
67346
68299
  }, [view.type, setSize]);
@@ -67498,10 +68451,7 @@ var SubagentStatusBar = memo6(function SubagentStatusBar2({
67498
68451
  });
67499
68452
 
67500
68453
  // src/tui/components/operator-dashboard/index.tsx
67501
- import { jsxDEV as jsxDEV56, Fragment as Fragment6 } from "@opentui/react/jsx-dev-runtime";
67502
- function markInFlightToolsErrored(messages, result) {
67503
- return messages.map((m2) => isToolMessage(m2) && (m2.status === "pending" || m2.status === "streaming") ? { ...m2, status: "error", result } : m2);
67504
- }
68454
+ import { jsxDEV as jsxDEV56, Fragment as Fragment7 } from "@opentui/react/jsx-dev-runtime";
67505
68455
  function OperatorDashboard({
67506
68456
  sessionId,
67507
68457
  initialMessage,
@@ -67516,10 +68466,8 @@ function OperatorDashboard({
67516
68466
  isModelUserSelected,
67517
68467
  setThinking,
67518
68468
  setIsExecuting,
67519
- tokenUsage,
67520
- addTokenUsage,
67521
- addCacheUsage,
67522
- resetTokenUsage,
68469
+ usageStore,
68470
+ markExecuted,
67523
68471
  setSessionCwd,
67524
68472
  reasoningEnabled,
67525
68473
  openAIReasoningEffort
@@ -67579,7 +68527,7 @@ function OperatorDashboard({
67579
68527
  });
67580
68528
  const commandCancelledRef = useRef21(false);
67581
68529
  const subagentStore = useMemo22(() => createSubagentStore(), []);
67582
- const subagentSessions = useSyncExternalStore2(subagentStore.subscribe, subagentStore.getSnapshot);
68530
+ const subagentSessions = useSyncExternalStore3(subagentStore.subscribe, subagentStore.getSnapshot);
67583
68531
  const subagentHelpers = useMemo22(() => createSubagentSessionHelpers(subagentStore.setState), [subagentStore]);
67584
68532
  const messageCountAtSubagentDoneRef = useRef21(null);
67585
68533
  const hasRunningSubagent = useMemo22(() => Array.from(subagentSessions.values()).some((s) => s.status === "running"), [subagentSessions]);
@@ -67592,7 +68540,7 @@ function OperatorDashboard({
67592
68540
  messageCountAtSubagentDoneRef.current = null;
67593
68541
  }
67594
68542
  }, [subagentSessions, hasRunningSubagent]);
67595
- const openSubagentDialog = useCallback20(() => {
68543
+ const openSubagentDialog = useCallback22(() => {
67596
68544
  replaceDialog(/* @__PURE__ */ jsxDEV56(SubagentDialog, {
67597
68545
  store: subagentStore
67598
68546
  }, undefined, false, undefined, this), {
@@ -67603,7 +68551,6 @@ function OperatorDashboard({
67603
68551
  const [messages, setMessages] = useState39([]);
67604
68552
  const displayMessagesRef = useRef21([]);
67605
68553
  displayMessagesRef.current = messages;
67606
- const textRef = useRef21("");
67607
68554
  const conversationRef = useRef21([]);
67608
68555
  const [inputValue, setInputValue] = useState39("");
67609
68556
  const [queuedMessages, setQueuedMessages] = useState39([]);
@@ -67644,10 +68591,6 @@ function OperatorDashboard({
67644
68591
  useEffect25(() => {
67645
68592
  approvedPlanRef.current = approvedPlanContent;
67646
68593
  }, [approvedPlanContent]);
67647
- const tokenUsageRef = useRef21(tokenUsage);
67648
- useEffect25(() => {
67649
- tokenUsageRef.current = tokenUsage;
67650
- }, [tokenUsage]);
67651
68594
  useEffect25(() => {
67652
68595
  const gate = approvalGateRef.current;
67653
68596
  const onApprovalNeeded = () => {
@@ -67766,188 +68709,63 @@ function OperatorDashboard({
67766
68709
  refocusPrompt();
67767
68710
  }
67768
68711
  }, [loading, refocusPrompt]);
68712
+ useEffect25(() => {
68713
+ if (sessionId === undefined)
68714
+ usageStore.beginNewSession();
68715
+ }, [sessionId, usageStore]);
67769
68716
  useEffect25(() => {
67770
68717
  if (!session || !sessionId)
67771
68718
  return;
67772
- resetTokenUsage();
67773
- tokenUsageRef.current = {
67774
- inputTokens: 0,
67775
- outputTokens: 0,
67776
- totalTokens: 0,
67777
- cachedTokens: 0,
67778
- cacheWriteTokens: 0
67779
- };
67780
68719
  const metrics = readExecutionMetrics(session.rootPath);
67781
- const persisted = metrics?.tokenUsage;
67782
- if (persisted && (persisted.inputTokens > 0 || persisted.outputTokens > 0)) {
67783
- addTokenUsage(persisted.inputTokens, persisted.outputTokens);
67784
- tokenUsageRef.current = {
67785
- ...persisted,
67786
- cachedTokens: 0,
67787
- cacheWriteTokens: 0
67788
- };
67789
- }
67790
- try {
67791
- writeExecutionMetrics({
67792
- sessionRootPath: session.rootPath,
67793
- tokenUsage: tokenUsageRef.current
67794
- });
67795
- } catch {}
67796
- }, [session, sessionId, addTokenUsage, resetTokenUsage]);
67797
- const appendText = useCallback20((text) => {
67798
- textRef.current += text;
67799
- const accumulated = textRef.current;
67800
- setMessages((prev) => {
67801
- const last = prev[prev.length - 1];
67802
- if (last && last.role === "assistant") {
67803
- const updated = [...prev];
67804
- updated[updated.length - 1] = { ...last, content: accumulated };
67805
- return updated;
67806
- }
67807
- return [
67808
- ...prev,
67809
- { role: "assistant", content: accumulated, createdAt: new Date }
67810
- ];
67811
- });
67812
- }, []);
67813
- const toolArgsDeltaRef = useRef21(new Map);
67814
- const addStreamingToolCall = useCallback20((toolCallId, toolName) => {
67815
- textRef.current = "";
67816
- toolArgsDeltaRef.current.set(toolCallId, {
67817
- toolName,
67818
- accumulated: ""
67819
- });
67820
- setMessages((prev) => [
67821
- ...prev,
67822
- {
67823
- role: "tool",
67824
- content: "",
67825
- createdAt: new Date,
67826
- toolCallId,
67827
- toolName,
67828
- args: {},
67829
- status: "streaming"
67830
- }
67831
- ]);
67832
- }, []);
67833
- const appendToolCallDelta = useCallback20((toolCallId, argsTextDelta) => {
67834
- const entry = toolArgsDeltaRef.current.get(toolCallId);
67835
- const accumulated = (entry?.accumulated ?? "") + argsTextDelta;
67836
- toolArgsDeltaRef.current.set(toolCallId, {
67837
- toolName: entry?.toolName ?? "",
67838
- accumulated
67839
- });
67840
- const parsed = tryParsePartialJson(accumulated);
67841
- if (!parsed)
67842
- return;
67843
- const contentText = extractStreamableContent(parsed);
67844
- const logs = contentText ? contentText.split(`
67845
- `) : undefined;
67846
- setMessages((msgs) => {
67847
- const idx = msgs.findIndex((m2) => isToolMessage(m2) && m2.toolCallId === toolCallId);
67848
- if (idx === -1)
67849
- return msgs;
67850
- const updated = [...msgs];
67851
- updated[idx] = { ...updated[idx], args: parsed, ...logs && { logs } };
67852
- return updated;
67853
- });
67854
- }, []);
67855
- const addToolCall = useCallback20((toolCallId, toolName, args) => {
67856
- textRef.current = "";
67857
- toolArgsDeltaRef.current.delete(toolCallId);
67858
- setMessages((prev) => {
67859
- const idx = prev.findIndex((m2) => isToolMessage(m2) && m2.toolCallId === toolCallId);
67860
- if (idx !== -1) {
67861
- const updated = [...prev];
67862
- updated[idx] = {
67863
- ...updated[idx],
67864
- args,
67865
- logs: undefined,
67866
- status: "pending"
67867
- };
67868
- return updated;
67869
- }
67870
- return [
67871
- ...prev,
67872
- {
67873
- role: "tool",
67874
- content: "",
67875
- createdAt: new Date,
67876
- toolCallId,
67877
- toolName,
67878
- args,
67879
- status: "pending"
67880
- }
67881
- ];
68720
+ usageStore.enterSession(session.id, {
68721
+ ...metrics?.tokenUsage ? { tokenUsage: metrics.tokenUsage } : {},
68722
+ ...metrics?.contextUsage ? { contextUsage: metrics.contextUsage } : {}
67882
68723
  });
67883
- }, []);
67884
- const updateToolResult = useCallback20((toolCallId, _toolName, result) => {
67885
- textRef.current = "";
67886
- setMessages((prev) => {
67887
- const idx = prev.findIndex((m2) => isToolMessage(m2) && m2.toolCallId === toolCallId);
67888
- if (idx === -1)
67889
- return prev;
67890
- const updated = [...prev];
67891
- updated[idx] = { ...updated[idx], status: "completed", result };
67892
- return updated;
67893
- });
67894
- }, []);
67895
- const cmdOutputBufRef = useRef21("");
67896
- const cmdFlushTimerRef = useRef21(null);
67897
- const MAX_LOG_LINES = 200;
67898
- const flushCommandOutput = useCallback20(() => {
67899
- const buf = cmdOutputBufRef.current;
67900
- if (!buf)
67901
- return;
67902
- cmdOutputBufRef.current = "";
67903
- setMessages((prev) => {
67904
- const idx = prev.findLastIndex((m2) => isToolMessage(m2) && (m2.status === "pending" || m2.status === "streaming"));
67905
- if (idx === -1)
67906
- return prev;
67907
- const msg = prev[idx];
67908
- const existing = msg.logs ?? [];
67909
- const incoming = buf.split(`
67910
- `);
67911
- let merged;
67912
- if (existing.length > 0 && !buf.startsWith(`
67913
- `)) {
67914
- merged = [...existing];
67915
- merged[merged.length - 1] += incoming[0];
67916
- merged.push(...incoming.slice(1));
67917
- } else {
67918
- merged = [...existing, ...incoming];
67919
- }
67920
- if (merged.length > MAX_LOG_LINES) {
67921
- merged = merged.slice(-MAX_LOG_LINES);
67922
- }
67923
- const updated = [...prev];
67924
- updated[idx] = { ...msg, logs: merged };
67925
- return updated;
67926
- });
67927
- }, []);
67928
- const onCommandOutput = useCallback20((data) => {
67929
- cmdOutputBufRef.current += data;
67930
- if (!cmdFlushTimerRef.current) {
67931
- cmdFlushTimerRef.current = setInterval(() => {
67932
- flushCommandOutput();
67933
- }, 150);
67934
- }
67935
- }, [flushCommandOutput]);
68724
+ }, [session, sessionId, usageStore]);
68725
+ const runSessionIdRef = useRef21(null);
68726
+ const displayEvents = useMemo22(() => createDisplayEventHandlers({
68727
+ updateMessages: setMessages,
68728
+ setThinking,
68729
+ setError
68730
+ }), [setThinking]);
67936
68731
  useEffect25(() => {
67937
68732
  return () => {
67938
- if (cmdFlushTimerRef.current) {
67939
- clearInterval(cmdFlushTimerRef.current);
67940
- cmdFlushTimerRef.current = null;
67941
- }
68733
+ displayEvents.dispose();
67942
68734
  };
68735
+ }, [displayEvents]);
68736
+ const updateWorkflowData = useCallback22((updater) => {
68737
+ setMessages((prev) => updateWorkflowDataMessage(prev, updater));
67943
68738
  }, []);
67944
- const handleApprove = useCallback20(() => {
68739
+ const runEventProjections = useMemo22(() => createRunEventProjections({
68740
+ display: displayEvents,
68741
+ subagents: subagentHelpers,
68742
+ updateWorkflowData,
68743
+ clearSubagentSessions: () => subagentStore.setState(new Map),
68744
+ questions: {
68745
+ onAsked: (toolCallId, questions) => {
68746
+ pendingToolCallIdRef.current = toolCallId;
68747
+ setPendingQuestions(questions);
68748
+ },
68749
+ onCleared: () => {
68750
+ pendingToolCallIdRef.current = null;
68751
+ setPendingQuestions(null);
68752
+ }
68753
+ },
68754
+ onRootToolCallStarted: () => {
68755
+ commandCancelledRef.current = false;
68756
+ },
68757
+ onPlanSubmitted: () => {
68758
+ planSubmittedRef.current = true;
68759
+ planGateBypassedOnResumeRef.current = false;
68760
+ }
68761
+ }), [displayEvents, subagentHelpers, updateWorkflowData, subagentStore]);
68762
+ const handleApprove = useCallback22(() => {
67945
68763
  const pending = approvalGateRef.current.getPendingApprovals();
67946
68764
  if (pending.length > 0) {
67947
68765
  approvalGateRef.current.approve(pending[0].id);
67948
68766
  }
67949
68767
  }, []);
67950
- const handleAutoApprove = useCallback20(() => {
68768
+ const handleAutoApprove = useCallback22(() => {
67951
68769
  setOperatorMode("auto");
67952
68770
  setOperatorState((prev) => ({
67953
68771
  ...prev,
@@ -67960,17 +68778,18 @@ function OperatorDashboard({
67960
68778
  approvalGateRef.current.approve(p.id);
67961
68779
  }
67962
68780
  }, []);
67963
- const runAgent = useCallback20(async (prompt) => {
68781
+ const runAgent = useCallback22(async (prompt) => {
67964
68782
  if (abortControllerRef.current) {
67965
68783
  abortControllerRef.current.abort();
67966
68784
  abortControllerRef.current = null;
67967
68785
  }
67968
68786
  const gen = ++generationRef.current;
68787
+ runSessionIdRef.current = sessionRef.current?.id ?? session?.id ?? null;
67969
68788
  setStatus("running");
67970
68789
  setThinking(true);
67971
68790
  setIsExecuting(true);
67972
68791
  setError(null);
67973
- textRef.current = "";
68792
+ displayEvents.resetPartialText();
67974
68793
  const controller = new AbortController;
67975
68794
  abortControllerRef.current = controller;
67976
68795
  const prevMessages = conversationRef.current;
@@ -67987,259 +68806,58 @@ function OperatorDashboard({
67987
68806
  conversationRef.current = nextMessages;
67988
68807
  if (sessionRef.current) {
67989
68808
  try {
67990
- const mp = join3(sessionRef.current.rootPath, "messages.json");
67991
- writeFileSync(mp, JSON.stringify(nextMessages, null, 2));
68809
+ const mp = join4(sessionRef.current.rootPath, "messages.json");
68810
+ writeFileSync2(mp, JSON.stringify(nextMessages, null, 2));
67992
68811
  } catch {}
67993
68812
  }
67994
68813
  } else {
67995
68814
  nextMessages = conversationRef.current;
67996
68815
  }
67997
68816
  const recordTokenUsage = (inputTokens, outputTokens, cacheReadTokens = 0, cacheWriteTokens = 0) => {
67998
- const nextUsage = accumulateTokenUsage(tokenUsageRef.current, inputTokens, outputTokens);
67999
- if (nextUsage) {
68000
- tokenUsageRef.current = nextUsage;
68001
- addTokenUsage(inputTokens, outputTokens);
68002
- if (session) {
68003
- try {
68004
- writeExecutionMetrics({
68005
- sessionRootPath: session.rootPath,
68006
- tokenUsage: nextUsage
68007
- });
68008
- } catch {}
68009
- }
68817
+ if (inputTokens > 0 || outputTokens > 0)
68818
+ markExecuted();
68819
+ const runSessionId = runSessionIdRef.current;
68820
+ usageStore.addSessionTokens(runSessionId, {
68821
+ inputTokens,
68822
+ outputTokens,
68823
+ cacheReadTokens,
68824
+ cacheWriteTokens
68825
+ });
68826
+ const activeSession = sessionRef.current;
68827
+ if (activeSession) {
68828
+ try {
68829
+ const usage = usageStore.getSnapshot(activeSession.id);
68830
+ writeExecutionMetrics({
68831
+ sessionRootPath: activeSession.rootPath,
68832
+ tokenUsage: usage.tokenUsage,
68833
+ ...usage.contextUsage ? { contextUsage: usage.contextUsage } : {}
68834
+ });
68835
+ } catch {}
68010
68836
  }
68011
- if (cacheReadTokens > 0 || cacheWriteTokens > 0) {
68012
- addCacheUsage(cacheReadTokens, cacheWriteTokens);
68837
+ };
68838
+ const runModelId = model.id;
68839
+ const recordRootStepUsage = (usage) => {
68840
+ const inputTokens = usage.inputTokens ?? 0;
68841
+ if (inputTokens > 0) {
68842
+ usageStore.setRootContext(runSessionIdRef.current, {
68843
+ usedTokens: inputTokens,
68844
+ contextLimit: getContextWindow(runModelId),
68845
+ modelId: runModelId
68846
+ });
68013
68847
  }
68848
+ recordTokenUsage(inputTokens, usage.outputTokens ?? 0);
68014
68849
  };
68015
68850
  const onStepFinish = (event) => {
68016
- recordTokenUsage(event.usage?.inputTokens ?? 0, event.usage?.outputTokens ?? 0);
68851
+ recordRootStepUsage(event.usage ?? {});
68017
68852
  };
68018
68853
  const eventBus = new AgentEventBus;
68019
- const isPentestAgent = (id) => id !== undefined && /^pentest-agent-\d+$/.test(id);
68020
- const updateWorkflowData = (updater) => {
68021
- setMessages((prev) => {
68022
- const idx = prev.findLastIndex((m2) => isToolMessage(m2) && m2.toolName === "run_pentest_workflow" && (m2.status === "pending" || m2.status === "streaming"));
68023
- if (idx === -1)
68024
- return prev;
68025
- const msg = prev[idx];
68026
- const wd = msg.workflowData ?? {
68027
- currentPhase: "discovery",
68028
- discovery: { label: "", status: "pending", logs: [] },
68029
- pentesting: {
68030
- label: "",
68031
- status: "idle",
68032
- subagents: {}
68033
- },
68034
- reporting: { label: "", status: "idle" }
68035
- };
68036
- const updated = [...prev];
68037
- updated[idx] = { ...msg, workflowData: updater(wd) };
68038
- return updated;
68039
- });
68040
- };
68041
- eventBus.on("text-delta", (d2) => {
68042
- if (gen !== generationRef.current)
68043
- return;
68044
- if (d2.subagentId) {
68045
- subagentHelpers.appendText(d2.subagentId, d2.text);
68046
- return;
68047
- }
68048
- setThinking(false);
68049
- appendText(d2.text);
68854
+ const runTrace = new RunTraceSession(eventBus, {
68855
+ isCurrent: () => gen === generationRef.current,
68856
+ recordTokenUsage
68050
68857
  });
68051
- eventBus.on("tool-call-start", (d2) => {
68052
- if (gen !== generationRef.current)
68053
- return;
68054
- if (d2.subagentId) {
68055
- subagentHelpers.addStreamingToolCall(d2.subagentId, d2.toolCallId, d2.toolName);
68056
- return;
68057
- }
68058
- setThinking(false);
68059
- addStreamingToolCall(d2.toolCallId, d2.toolName);
68060
- });
68061
- eventBus.on("tool-call-delta", (d2) => {
68062
- if (gen !== generationRef.current)
68063
- return;
68064
- if (d2.subagentId) {
68065
- subagentHelpers.appendToolCallDelta(d2.subagentId, d2.toolCallId, d2.argsTextDelta);
68066
- return;
68067
- }
68068
- appendToolCallDelta(d2.toolCallId, d2.argsTextDelta);
68069
- });
68070
- eventBus.on("tool-call-complete", (d2) => {
68071
- if (gen !== generationRef.current)
68072
- return;
68073
- if (d2.subagentId) {
68074
- const args2 = d2.args && typeof d2.args === "object" && !Array.isArray(d2.args) && d2.args !== null ? d2.args : {};
68075
- subagentHelpers.addToolCall(d2.subagentId, d2.toolCallId, d2.toolName, args2);
68076
- return;
68077
- }
68078
- setThinking(false);
68079
- commandCancelledRef.current = false;
68080
- const args = d2.args && typeof d2.args === "object" && !Array.isArray(d2.args) && d2.args !== null ? d2.args : undefined;
68081
- addToolCall(d2.toolCallId, d2.toolName, args);
68082
- if (d2.toolName === ASK_USER_QUESTIONS_TOOL_NAME && args) {
68083
- const rawQuestions = args.questions;
68084
- if (Array.isArray(rawQuestions) && rawQuestions.length > 0) {
68085
- pendingToolCallIdRef.current = d2.toolCallId;
68086
- setPendingQuestions(rawQuestions);
68087
- }
68088
- }
68089
- });
68090
- eventBus.on("tool-result", (d2) => {
68091
- if (gen !== generationRef.current)
68092
- return;
68093
- if (d2.subagentId) {
68094
- subagentHelpers.updateToolResult(d2.subagentId, d2.toolCallId, d2.result);
68095
- return;
68096
- }
68097
- flushCommandOutput();
68098
- if (cmdFlushTimerRef.current) {
68099
- clearInterval(cmdFlushTimerRef.current);
68100
- cmdFlushTimerRef.current = null;
68101
- }
68102
- setThinking(true);
68103
- updateToolResult(d2.toolCallId, d2.toolName, d2.result);
68104
- if (d2.toolName === "submit_plan" && d2.result?.success === true) {
68105
- planSubmittedRef.current = true;
68106
- planGateBypassedOnResumeRef.current = false;
68107
- }
68108
- });
68109
- eventBus.on("command-output", (d2) => {
68110
- if (gen !== generationRef.current)
68111
- return;
68112
- if (d2.subagentId)
68113
- return;
68114
- onCommandOutput(d2.data);
68115
- });
68116
- eventBus.on("error", (d2) => {
68117
- if (gen !== generationRef.current)
68118
- return;
68119
- if (d2.subagentId) {
68120
- const errMsg = d2.error instanceof Error ? d2.error.message : "Unknown error";
68121
- subagentHelpers.appendText(d2.subagentId, `
68122
- Error: ${errMsg}
68123
- `);
68124
- return;
68125
- }
68126
- console.error("Agent error:", d2.error);
68127
- pendingToolCallIdRef.current = null;
68128
- setPendingQuestions(null);
68129
- const errorMessage = d2.error instanceof Error ? d2.error.message : "Unknown error";
68130
- setError(errorMessage);
68131
- setMessages((prev) => markInFlightToolsErrored(prev, errorMessage));
68132
- });
68133
- eventBus.on("subagent-spawn", ({ subagentId, name }) => {
68134
- if (gen !== generationRef.current)
68135
- return;
68136
- if (subagentId.startsWith("app:"))
68137
- return;
68138
- subagentHelpers.spawnSession(subagentId, name);
68139
- if (!isPentestAgent(subagentId))
68140
- return;
68141
- updateWorkflowData((wd) => ({
68142
- ...wd,
68143
- pentesting: {
68144
- ...wd.pentesting,
68145
- subagents: {
68146
- ...wd.pentesting.subagents,
68147
- [subagentId]: {
68148
- name,
68149
- status: "pending",
68150
- logs: []
68151
- }
68152
- }
68153
- }
68154
- }));
68155
- });
68156
- eventBus.on("subagent-complete", ({ subagentId, status: status2 }) => {
68157
- if (gen !== generationRef.current)
68158
- return;
68159
- if (subagentId.startsWith("app:"))
68160
- return;
68161
- subagentHelpers.completeSession(subagentId, status2);
68162
- if (!isPentestAgent(subagentId))
68163
- return;
68164
- updateWorkflowData((wd) => {
68165
- const entry = wd.pentesting.subagents[subagentId];
68166
- if (!entry)
68167
- return wd;
68168
- return {
68169
- ...wd,
68170
- pentesting: {
68171
- ...wd.pentesting,
68172
- subagents: {
68173
- ...wd.pentesting.subagents,
68174
- [subagentId]: { ...entry, status: status2 }
68175
- }
68176
- }
68177
- };
68178
- });
68179
- });
68180
- eventBus.on("workflow-phase-start", (d2) => {
68181
- if (gen !== generationRef.current)
68182
- return;
68183
- textRef.current = "";
68184
- if (d2.phase === "discovery") {
68185
- subagentStore.setState(new Map);
68186
- }
68187
- updateWorkflowData((wd) => {
68188
- const next = {
68189
- ...wd,
68190
- currentPhase: d2.phase
68191
- };
68192
- if (d2.phase === "discovery") {
68193
- next.discovery = {
68194
- ...wd.discovery,
68195
- label: d2.label,
68196
- status: "pending",
68197
- logs: []
68198
- };
68199
- } else if (d2.phase === "pentesting") {
68200
- next.pentesting = {
68201
- ...wd.pentesting,
68202
- label: d2.label,
68203
- status: "pending"
68204
- };
68205
- } else if (d2.phase === "reporting") {
68206
- next.reporting = {
68207
- ...wd.reporting,
68208
- label: d2.label,
68209
- status: "pending"
68210
- };
68211
- }
68212
- return next;
68213
- });
68214
- });
68215
- eventBus.on("workflow-phase-complete", (d2) => {
68216
- if (gen !== generationRef.current)
68217
- return;
68218
- textRef.current = "";
68219
- updateWorkflowData((wd) => {
68220
- const next = { ...wd };
68221
- if (d2.phase === "discovery") {
68222
- const targets = d2.summary.targets ?? [];
68223
- next.discovery = {
68224
- ...wd.discovery,
68225
- status: "complete",
68226
- targets,
68227
- cached: d2.summary.cached
68228
- };
68229
- } else if (d2.phase === "pentesting") {
68230
- next.pentesting = { ...wd.pentesting, status: "complete" };
68231
- } else if (d2.phase === "reporting") {
68232
- next.reporting = {
68233
- ...wd.reporting,
68234
- status: "complete",
68235
- findingsCount: d2.summary.findingsCount,
68236
- findingsBySeverity: d2.summary.findingsBySeverity,
68237
- reportPath: d2.summary.reportPath
68238
- };
68239
- next.currentPhase = "complete";
68240
- }
68241
- return next;
68242
- });
68858
+ const unbindRunEvents = bindOperatorRunEvents(eventBus, {
68859
+ isCurrent: () => gen === generationRef.current,
68860
+ handlers: runEventProjections.handlers
68243
68861
  });
68244
68862
  const skillsCatalog = skillsRegistry.buildCatalog() || undefined;
68245
68863
  const commonInput = {
@@ -68267,59 +68885,24 @@ Error: ${errMsg}
68267
68885
  promptInjectionLibrarySource: initialConfig?.promptInjectionLibrarySource ?? (route.data.type === "operator" ? route.data.initialSkill?.args?.library : undefined),
68268
68886
  onStepFinish,
68269
68887
  onCacheMetrics: (metrics) => {
68270
- addCacheUsage(metrics.cacheReadInputTokens, metrics.cacheCreationInputTokens);
68888
+ usageStore.addSessionTokens(runSessionIdRef.current, {
68889
+ cacheReadTokens: metrics.cacheReadInputTokens,
68890
+ cacheWriteTokens: metrics.cacheCreationInputTokens
68891
+ });
68271
68892
  },
68272
68893
  eventBus,
68273
68894
  onSessionReady: (s) => {
68274
68895
  setSessionCwd(s.rootPath);
68275
68896
  sessionRef.current = s;
68276
68897
  setSession((prev) => prev ?? s);
68277
- tryAttachWandb(s);
68278
- }
68279
- };
68280
- let wandbCleanup = null;
68281
- let earlyBuffer = [];
68282
- const earlyBufferHandler = (e) => {
68283
- earlyBuffer?.push(e);
68284
- };
68285
- eventBus.on("trace-record", earlyBufferHandler);
68286
- const countedSubagentSteps = new Set;
68287
- eventBus.on("trace-record", ({ record, subagentId }) => {
68288
- if (gen !== generationRef.current)
68289
- return;
68290
- if (!subagentId || record.type !== "step")
68291
- return;
68292
- const key = `${subagentId}:${record.stepIndex}:${record.timestamp}`;
68293
- if (countedSubagentSteps.has(key))
68294
- return;
68295
- countedSubagentSteps.add(key);
68296
- recordTokenUsage(record.usage.inputTokens, record.usage.outputTokens, record.usage.cacheReadTokens ?? 0, record.usage.cacheWriteTokens ?? 0);
68297
- });
68298
- const tryAttachWandb = async (s) => {
68299
- if (wandbCleanup)
68300
- return;
68301
- const cleanup = await attachWandbToEventBus(s, eventBus).catch((e) => {
68302
- console.error("[wandb] Attach failed:", e);
68303
- return null;
68304
- });
68305
- if (cleanup) {
68306
- wandbCleanup = cleanup;
68307
- const buffered = earlyBuffer;
68308
- earlyBuffer = null;
68309
- eventBus.off("trace-record", earlyBufferHandler);
68310
- if (buffered) {
68311
- for (const e of buffered) {
68312
- eventBus.emit("trace-record", e);
68313
- }
68314
- }
68315
- } else {
68316
- earlyBuffer = null;
68317
- eventBus.off("trace-record", earlyBufferHandler);
68898
+ runSessionIdRef.current = s.id;
68899
+ usageStore.enterSession(s.id);
68900
+ runTrace.tryAttach(s);
68318
68901
  }
68319
68902
  };
68320
68903
  const wandbSession = session ?? sessionRef.current;
68321
68904
  if (wandbSession) {
68322
- await tryAttachWandb(wandbSession);
68905
+ await runTrace.tryAttach(wandbSession);
68323
68906
  }
68324
68907
  try {
68325
68908
  let agentResult;
@@ -68374,9 +68957,9 @@ Error: ${errMsg}
68374
68957
  if (gen === generationRef.current) {
68375
68958
  try {
68376
68959
  const rootPath = agentResult.session.rootPath;
68377
- const mp = join3(rootPath, "messages.json");
68378
- if (existsSync3(mp)) {
68379
- const raw = JSON.parse(readFileSync3(mp, "utf-8"));
68960
+ const mp = join4(rootPath, "messages.json");
68961
+ if (existsSync4(mp)) {
68962
+ const raw = JSON.parse(readFileSync4(mp, "utf-8"));
68380
68963
  if (Array.isArray(raw) && raw.length > 0) {
68381
68964
  conversationRef.current = normalizeMessages(sessions.getResumeMessages(raw));
68382
68965
  }
@@ -68402,8 +68985,8 @@ Error: ${errMsg}
68402
68985
  setPendingQuestions(null);
68403
68986
  if (sessionRef.current) {
68404
68987
  try {
68405
- const mp = join3(sessionRef.current.rootPath, "messages.json");
68406
- writeFileSync(mp, JSON.stringify(prevMessages, null, 2));
68988
+ const mp = join4(sessionRef.current.rootPath, "messages.json");
68989
+ writeFileSync2(mp, JSON.stringify(prevMessages, null, 2));
68407
68990
  } catch {}
68408
68991
  }
68409
68992
  const errorMsg = e instanceof Error ? e.message : "Agent failed";
@@ -68418,12 +69001,8 @@ Error: ${errMsg}
68418
69001
  ]);
68419
69002
  }
68420
69003
  } finally {
68421
- earlyBuffer = null;
68422
- eventBus.off("trace-record", earlyBufferHandler);
68423
- if (wandbCleanup) {
68424
- const fn = wandbCleanup;
68425
- await fn().catch((e) => console.error("[wandb] Flush failed:", e));
68426
- }
69004
+ unbindRunEvents();
69005
+ await runTrace.cleanupRun();
68427
69006
  if (gen === generationRef.current) {
68428
69007
  setStatus(pendingToolCallIdRef.current ? "waiting" : "idle");
68429
69008
  setThinking(false);
@@ -68442,18 +69021,10 @@ Error: ${errMsg}
68442
69021
  operatorState,
68443
69022
  operatorMode,
68444
69023
  agentMode,
68445
- addTokenUsage,
68446
- appendText,
68447
- addStreamingToolCall,
68448
- appendToolCallDelta,
68449
- addToolCall,
68450
- updateToolResult,
68451
- flushCommandOutput,
68452
- onCommandOutput,
68453
- subagentHelpers,
69024
+ displayEvents,
69025
+ runEventProjections,
68454
69026
  setThinking,
68455
69027
  setIsExecuting,
68456
- addCacheUsage,
68457
69028
  initialConfig?.sandbox,
68458
69029
  initialConfig?.taskDriven,
68459
69030
  initialConfig?.target,
@@ -68462,14 +69033,15 @@ Error: ${errMsg}
68462
69033
  strikeMode,
68463
69034
  route.data,
68464
69035
  setSessionCwd,
68465
- subagentStore.setState,
68466
69036
  skillsRegistry.buildCatalog,
68467
69037
  requireApproval,
68468
69038
  skillsRegistry,
68469
69039
  reasoningEnabled,
68470
- openAIReasoningEffort
69040
+ openAIReasoningEffort,
69041
+ usageStore,
69042
+ markExecuted
68471
69043
  ]);
68472
- const handleSubmit = useCallback20((value) => {
69044
+ const handleSubmit = useCallback22((value) => {
68473
69045
  const pending = approvalGateRef.current.getPendingApprovals();
68474
69046
  const result = resolveSubmit(value, status, pending.length > 0);
68475
69047
  if (result.denyPending) {
@@ -68607,16 +69179,16 @@ ${content}
68607
69179
  planApprovedPendingRunRef.current = false;
68608
69180
  runAgentRef.current("Proceed with the approved plan.");
68609
69181
  }, [operatorMode]);
68610
- const addSystemMessage = useCallback20((content) => {
69182
+ const addSystemMessage = useCallback22((content) => {
68611
69183
  setMessages((prev) => [
68612
69184
  ...prev,
68613
69185
  { role: "system", content, createdAt: new Date }
68614
69186
  ]);
68615
69187
  }, []);
68616
- const showModelPicker = useCallback20(() => {
69188
+ const showModelPicker = useCallback22(() => {
68617
69189
  executeCommand("/models");
68618
69190
  }, [executeCommand]);
68619
- const handleHeadersSlash = useCallback20(async (op) => {
69191
+ const handleHeadersSlash = useCallback22(async (op) => {
68620
69192
  const active = sessionRef.current;
68621
69193
  if (!active) {
68622
69194
  addSystemMessage("No active session — cannot manage headers yet.");
@@ -68687,7 +69259,7 @@ ${formatParseError(parsed.error)}`);
68687
69259
  }
68688
69260
  }
68689
69261
  }, [addSystemMessage]);
68690
- const handleCommandExecute = useCallback20(async (command) => {
69262
+ const handleCommandExecute = useCallback22(async (command) => {
68691
69263
  const action = routeCommand(command, resolveSkillContent);
68692
69264
  switch (action.type) {
68693
69265
  case "show-models":
@@ -68761,7 +69333,7 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
68761
69333
  handleHeadersSlash,
68762
69334
  strikeMode
68763
69335
  ]);
68764
- const handleAbort = useCallback20(() => {
69336
+ const handleAbort = useCallback22(() => {
68765
69337
  if (!abortControllerRef.current)
68766
69338
  return;
68767
69339
  const action = resolveAbortAction(commandCancelledRef.current, () => cancelHandleRef.current.cancel());
@@ -68789,77 +69361,15 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
68789
69361
  setThinking(false);
68790
69362
  setIsExecuting(false);
68791
69363
  setMessages((prev) => markInFlightToolsErrored(prev, "Interrupted"));
68792
- subagentStore.setState((prev) => {
68793
- let changed = false;
68794
- const next = new Map(prev);
68795
- for (const [id, session2] of next) {
68796
- const hasInFlight = session2.messages.some((m2) => m2.role === "tool" && (m2.status === "pending" || m2.status === "streaming"));
68797
- if (hasInFlight || session2.status === "running") {
68798
- changed = true;
68799
- next.set(id, {
68800
- ...session2,
68801
- status: session2.status === "running" ? "cancelled" : session2.status,
68802
- messages: session2.messages.map((m2) => m2.role === "tool" && (m2.status === "pending" || m2.status === "streaming") ? { ...m2, status: "error", result: "Interrupted" } : m2)
68803
- });
68804
- }
68805
- }
68806
- return changed ? next : prev;
68807
- });
69364
+ subagentStore.setState(markSubagentsInterrupted);
68808
69365
  const activeSession = sessionRef.current;
68809
69366
  if (activeSession) {
68810
- try {
68811
- const messagesPath = join3(activeSession.rootPath, "messages.json");
68812
- if (existsSync3(messagesPath)) {
68813
- const raw = JSON.parse(readFileSync3(messagesPath, "utf-8"));
68814
- if (Array.isArray(raw) && raw.length > 0) {
68815
- conversationRef.current = normalizeMessages(sessions.getResumeMessages(raw));
68816
- }
68817
- }
68818
- const last = conversationRef.current[conversationRef.current.length - 1];
68819
- if (last?.role === "user") {
68820
- const partial = textRef.current.trim();
68821
- const pendingTools = displayMessagesRef.current.filter((m2) => isToolMessage(m2) && (m2.status === "pending" || m2.status === "streaming"));
68822
- const assistantContent = [
68823
- {
68824
- type: "text",
68825
- text: partial || "[Response interrupted by user.]"
68826
- }
68827
- ];
68828
- for (const t of pendingTools) {
68829
- assistantContent.push({
68830
- type: "tool-call",
68831
- toolCallId: t.toolCallId,
68832
- toolName: t.toolName,
68833
- input: t.args ?? {}
68834
- });
68835
- }
68836
- conversationRef.current = [
68837
- ...conversationRef.current,
68838
- {
68839
- role: "assistant",
68840
- content: assistantContent
68841
- }
68842
- ];
68843
- if (pendingTools.length > 0) {
68844
- conversationRef.current = [
68845
- ...conversationRef.current,
68846
- {
68847
- role: "tool",
68848
- content: pendingTools.map((t) => ({
68849
- type: "tool-result",
68850
- toolCallId: t.toolCallId,
68851
- toolName: t.toolName ?? "unknown",
68852
- output: {
68853
- type: "text",
68854
- value: "Cancelled by user."
68855
- }
68856
- }))
68857
- }
68858
- ];
68859
- }
68860
- writeFileSync(join3(activeSession.rootPath, "messages.json"), JSON.stringify(conversationRef.current, null, 2));
68861
- }
68862
- } catch {}
69367
+ conversationRef.current = recoverAbortedTranscript({
69368
+ rootPath: activeSession.rootPath,
69369
+ conversation: conversationRef.current,
69370
+ partialText: displayEvents.getPartialText(),
69371
+ displayMessages: displayMessagesRef.current
69372
+ });
68863
69373
  }
68864
69374
  setMessages((prev) => {
68865
69375
  const updated = prev.map((m2) => isToolMessage(m2) && (m2.status === "pending" || m2.status === "streaming") ? { ...m2, status: "error", result: "Cancelled by user" } : m2);
@@ -68872,51 +69382,30 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
68872
69382
  }
68873
69383
  ];
68874
69384
  });
68875
- }, [setThinking, setIsExecuting, subagentStore.setState]);
68876
- const resumeWithQuestionResult = useCallback20((result) => {
69385
+ }, [setThinking, setIsExecuting, subagentStore.setState, displayEvents]);
69386
+ const resumeWithQuestionResult = useCallback22((result) => {
68877
69387
  const toolCallId = pendingToolCallIdRef.current;
68878
69388
  if (!toolCallId)
68879
69389
  return;
68880
- const wrappedOutput = {
68881
- type: "json",
68882
- value: result
68883
- };
68884
- const updated = conversationRef.current.map((msg) => {
68885
- if (msg.role !== "tool")
68886
- return msg;
68887
- if (!Array.isArray(msg.content))
68888
- return msg;
68889
- let mutated = false;
68890
- const nextContent = msg.content.map((part) => {
68891
- if (part && typeof part === "object" && "type" in part && part.type === "tool-result" && part.toolCallId === toolCallId) {
68892
- mutated = true;
68893
- return {
68894
- ...part,
68895
- output: wrappedOutput
68896
- };
68897
- }
68898
- return part;
68899
- });
68900
- return mutated ? { ...msg, content: nextContent } : msg;
68901
- });
69390
+ const updated = rewriteToolResultOutput(conversationRef.current, toolCallId, result);
68902
69391
  conversationRef.current = updated;
68903
69392
  const activeSession = sessionRef.current;
68904
69393
  if (activeSession) {
68905
69394
  try {
68906
- writeFileSync(join3(activeSession.rootPath, "messages.json"), JSON.stringify(updated, null, 2));
69395
+ writeFileSync2(join4(activeSession.rootPath, "messages.json"), JSON.stringify(updated, null, 2));
68907
69396
  } catch {}
68908
69397
  }
68909
69398
  pendingToolCallIdRef.current = null;
68910
69399
  setPendingQuestions(null);
68911
69400
  runAgentRef.current(null);
68912
69401
  }, []);
68913
- const handleQuestionsSubmit = useCallback20((answers) => {
69402
+ const handleQuestionsSubmit = useCallback22((answers) => {
68914
69403
  resumeWithQuestionResult({ answers, skipped: false });
68915
69404
  }, [resumeWithQuestionResult]);
68916
- const handleQuestionsSkip = useCallback20(() => {
69405
+ const handleQuestionsSkip = useCallback22(() => {
68917
69406
  resumeWithQuestionResult({ answers: [], skipped: true });
68918
69407
  }, [resumeWithQuestionResult]);
68919
- const transitionToMode = useCallback20((next) => {
69408
+ const transitionToMode = useCallback22((next) => {
68920
69409
  approvalGateRef.current.updateConfig({
68921
69410
  requireApproval: next === "manual"
68922
69411
  });
@@ -68931,7 +69420,7 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
68931
69420
  sessions.updateOperatorSettings(sid, { initialMode: next }).catch((e) => console.error("[operator] Failed to persist mode:", e));
68932
69421
  }
68933
69422
  }, []);
68934
- const cycleMode = useCallback20(() => {
69423
+ const cycleMode = useCallback22(() => {
68935
69424
  const current = operatorModeRef.current;
68936
69425
  const idx = OPERATOR_MODE_CYCLE.indexOf(current);
68937
69426
  const next = OPERATOR_MODE_CYCLE[(idx + 1) % OPERATOR_MODE_CYCLE.length];
@@ -68972,11 +69461,8 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
68972
69461
  const removeIdx = selectedQueueIndex;
68973
69462
  setQueuedMessages((prev) => prev.filter((_2, i) => i !== removeIdx));
68974
69463
  setSelectedQueueIndex(-1);
68975
- flushCommandOutput();
68976
- if (cmdFlushTimerRef.current) {
68977
- clearInterval(cmdFlushTimerRef.current);
68978
- cmdFlushTimerRef.current = null;
68979
- }
69464
+ displayEvents.flushCommandOutput();
69465
+ displayEvents.stopCommandOutputFlush();
68980
69466
  setMessages((prev) => prev.map((m2) => isToolMessage(m2) && m2.status === "pending" ? { ...m2, status: "error", result: "Interrupted" } : m2));
68981
69467
  runAgentRef.current(msg);
68982
69468
  return;
@@ -69021,34 +69507,15 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
69021
69507
  key.preventDefault?.();
69022
69508
  const toolCallId = pendingToolCallIdRef.current;
69023
69509
  if (toolCallId) {
69024
- const abortedResult = {
69025
- type: "json",
69026
- value: {
69027
- answers: [],
69028
- skipped: true,
69029
- aborted: true
69030
- }
69031
- };
69032
- conversationRef.current = conversationRef.current.map((msg) => {
69033
- if (msg.role !== "tool" || !Array.isArray(msg.content))
69034
- return msg;
69035
- let mutated = false;
69036
- const nextContent = msg.content.map((part) => {
69037
- if (part && typeof part === "object" && "type" in part && part.type === "tool-result" && part.toolCallId === toolCallId) {
69038
- mutated = true;
69039
- return {
69040
- ...part,
69041
- output: abortedResult
69042
- };
69043
- }
69044
- return part;
69045
- });
69046
- return mutated ? { ...msg, content: nextContent } : msg;
69510
+ conversationRef.current = rewriteToolResultOutput(conversationRef.current, toolCallId, {
69511
+ answers: [],
69512
+ skipped: true,
69513
+ aborted: true
69047
69514
  });
69048
69515
  const activeSession = sessionRef.current;
69049
69516
  if (activeSession) {
69050
69517
  try {
69051
- writeFileSync(join3(activeSession.rootPath, "messages.json"), JSON.stringify(conversationRef.current, null, 2));
69518
+ writeFileSync2(join4(activeSession.rootPath, "messages.json"), JSON.stringify(conversationRef.current, null, 2));
69052
69519
  } catch {}
69053
69520
  }
69054
69521
  }
@@ -69197,7 +69664,7 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
69197
69664
  fg: colors2.warning,
69198
69665
  children: "STRIKE"
69199
69666
  }, undefined, false, undefined, this),
69200
- (session?.targets[0] || initialConfig?.target) && /* @__PURE__ */ jsxDEV56(Fragment6, {
69667
+ (session?.targets[0] || initialConfig?.target) && /* @__PURE__ */ jsxDEV56(Fragment7, {
69201
69668
  children: [
69202
69669
  /* @__PURE__ */ jsxDEV56("text", {
69203
69670
  fg: colors2.textMuted,
@@ -69240,7 +69707,7 @@ ${planContent}` : "No plan exists yet. Switch to plan mode (Shift+Tab) to create
69240
69707
  questions: pendingQuestions,
69241
69708
  onSubmit: handleQuestionsSubmit,
69242
69709
  onSkip: handleQuestionsSkip
69243
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV56(Fragment6, {
69710
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV56(Fragment7, {
69244
69711
  children: [
69245
69712
  /* @__PURE__ */ jsxDEV56(QueuedMessages, {
69246
69713
  messages: queuedMessages,
@@ -69368,19 +69835,19 @@ function ResponsibleUseDisclosure({
69368
69835
 
69369
69836
  // src/tui/components/switch.tsx
69370
69837
  import React2 from "react";
69371
- import { jsxDEV as jsxDEV58, Fragment as Fragment7 } from "@opentui/react/jsx-dev-runtime";
69838
+ import { jsxDEV as jsxDEV58, Fragment as Fragment8 } from "@opentui/react/jsx-dev-runtime";
69372
69839
  var CaseSymbol = Symbol("Switch.Case");
69373
69840
  var DefaultSymbol = Symbol("Switch.Default");
69374
69841
  function CaseComponent({
69375
69842
  children
69376
69843
  }) {
69377
- return /* @__PURE__ */ jsxDEV58(Fragment7, {
69844
+ return /* @__PURE__ */ jsxDEV58(Fragment8, {
69378
69845
  children
69379
69846
  }, undefined, false, undefined, this);
69380
69847
  }
69381
69848
  CaseComponent[CaseSymbol] = true;
69382
69849
  function DefaultComponent({ children }) {
69383
- return /* @__PURE__ */ jsxDEV58(Fragment7, {
69850
+ return /* @__PURE__ */ jsxDEV58(Fragment8, {
69384
69851
  children
69385
69852
  }, undefined, false, undefined, this);
69386
69853
  }
@@ -69403,7 +69870,7 @@ function SwitchComponent({
69403
69870
  }
69404
69871
  }
69405
69872
  });
69406
- return /* @__PURE__ */ jsxDEV58(Fragment7, {
69873
+ return /* @__PURE__ */ jsxDEV58(Fragment8, {
69407
69874
  children: matchedChild || defaultChild
69408
69875
  }, undefined, false, undefined, this);
69409
69876
  }
@@ -69616,6 +70083,7 @@ function createKeybindings(deps) {
69616
70083
  setExternalDialogOpen,
69617
70084
  setFocusIndex,
69618
70085
  navigableItems,
70086
+ onExit,
69619
70087
  setShowToolsPanel
69620
70088
  } = deps;
69621
70089
  const route = useRoute();
@@ -69631,9 +70099,7 @@ function createKeybindings(deps) {
69631
70099
  const now = Date.now();
69632
70100
  const lastPress = ctrlCPressTime;
69633
70101
  if (lastPress && now - lastPress < 1000) {
69634
- cleanupTerminalFocusMode();
69635
- renderer.destroy();
69636
- process.exit(0);
70102
+ await onExit();
69637
70103
  } else {
69638
70104
  setInputKey((prev) => prev + 1);
69639
70105
  setCtrlCPressTime(now);
@@ -69829,7 +70295,7 @@ import { jsxDEV as jsxDEV61 } from "@opentui/react/jsx-dev-runtime";
69829
70295
  installObfuscationTextPatch();
69830
70296
  extend({ frameBuffer: FrameBufferRenderable });
69831
70297
  extend({ frameBuffer: FrameBufferRenderable });
69832
- function App({ appConfig }) {
70298
+ function App({ appConfig, onExit }) {
69833
70299
  const [focusIndex, setFocusIndex] = useState40(0);
69834
70300
  const [cwd, setCwd] = useState40(process.cwd());
69835
70301
  const [ctrlCPressTime, setCtrlCPressTime] = useState40(null);
@@ -69885,7 +70351,8 @@ function App({ appConfig }) {
69885
70351
  setShowSessionsDialog,
69886
70352
  setShowShortcutsDialog,
69887
70353
  setFocusIndex,
69888
- navigableItems
70354
+ navigableItems,
70355
+ onExit
69889
70356
  },
69890
70357
  children: /* @__PURE__ */ jsxDEV61(AppContent, {
69891
70358
  focusIndex,
@@ -69982,7 +70449,7 @@ function AppContent({
69982
70449
  toast(`Update available: v${currentVersion} → v${latestVersion}. Run: pensar upgrade`, "warn", 8000);
69983
70450
  });
69984
70451
  const checkAuthToken = async () => {
69985
- const { isTokenExpired, isConnected: isConnected2 } = await import("./index-4ad3sdwk.js");
70452
+ const { isTokenExpired, isConnected: isConnected2 } = await import("./index-vvkvwmyr.js");
69986
70453
  if (isConnected2(config2.data) && config2.data.accessToken && isTokenExpired(config2.data.accessToken, 60) && !config2.data.refreshToken && !config2.data.pensarAPIKey) {
69987
70454
  toast("Your Pensar Console session has expired. Run /login to refresh.", "warn", 8000);
69988
70455
  }
@@ -70317,6 +70784,7 @@ async function main() {
70317
70784
  const transparent = appConfig.transparentBackground ?? false;
70318
70785
  const themeColors = resolveThemeColors(getTheme(themeName), mode, transparent);
70319
70786
  overlayThemeRef.current = themeColors;
70787
+ const observabilityRuntime = startObservabilityRuntime();
70320
70788
  const renderer = await createCliRenderer({
70321
70789
  exitOnCtrlC: false,
70322
70790
  consoleOptions: buildConsoleOptions(themeColors),
@@ -70324,10 +70792,17 @@ async function main() {
70324
70792
  });
70325
70793
  const { copyToClipboard } = createClipboardManager(renderer);
70326
70794
  setupAutoCopy(renderer, copyToClipboard);
70795
+ let cleanupPromise = null;
70327
70796
  const cleanup = () => {
70328
- cleanupTerminalFocusMode();
70329
- renderer.destroy();
70330
- process.exit(0);
70797
+ if (!cleanupPromise) {
70798
+ cleanupPromise = (async () => {
70799
+ cleanupTerminalFocusMode();
70800
+ renderer.destroy();
70801
+ await observabilityRuntime.shutdown().catch(() => {});
70802
+ process.exit(0);
70803
+ })();
70804
+ }
70805
+ return cleanupPromise;
70331
70806
  };
70332
70807
  process.on("SIGINT", cleanup);
70333
70808
  process.on("SIGTERM", cleanup);
@@ -70359,7 +70834,8 @@ async function main() {
70359
70834
  children: [
70360
70835
  /* @__PURE__ */ jsxDEV61(ErrorBoundary, {
70361
70836
  children: /* @__PURE__ */ jsxDEV61(App, {
70362
- appConfig
70837
+ appConfig,
70838
+ onExit: cleanup
70363
70839
  }, undefined, false, undefined, this)
70364
70840
  }, undefined, false, undefined, this),
70365
70841
  /* @__PURE__ */ jsxDEV61(ToastContainer, {}, undefined, false, undefined, this)