@crazyhappyone/dsh-tui 0.1.0-alpha.43 → 0.1.0-alpha.44

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.
package/dist/index.js CHANGED
@@ -69421,25 +69421,44 @@ function renderCode(value, width, rowOffset, sourceStart) {
69421
69421
  const lines = value.split("\n");
69422
69422
  const out = [];
69423
69423
  let row = rowOffset;
69424
+ const digits = Math.max(2, String(lines.length).length);
69425
+ const gutterWidth = digits + 4;
69426
+ const codeWidth = Math.max(1, width - gutterWidth);
69424
69427
  for (const [index2, line5] of lines.entries()) {
69425
- const wrapped = wrapDisplayLines(escapeContent(line5), Math.max(1, width - 2));
69428
+ const lineNum = index2 + 1;
69429
+ const gutterPrefix = ` ${String(lineNum).padStart(digits, " ")} \u2502 `;
69430
+ const continuationGutter = ` ${" ".repeat(digits)} \u2502 `;
69431
+ const wrapped = wrapDisplayLines(escapeContent(line5), codeWidth);
69426
69432
  if (wrapped.length === 0) {
69427
- out.push(freezeEmpty(row, index2 === 0 ? sourceStart : -1));
69433
+ const buffer = makeBuffer();
69434
+ appendSegment(buffer, { text: gutterPrefix, token: "fgDim", bold: false });
69435
+ const emptyLine2 = freezeLine(buffer, row, index2 === 0 ? sourceStart : -1);
69436
+ out.push({
69437
+ ...emptyLine2,
69438
+ background: "codeBg",
69439
+ backgroundColumns: Math.max(width, emptyLine2.displayWidth)
69440
+ });
69428
69441
  row += 1;
69429
69442
  continue;
69430
69443
  }
69431
69444
  for (const [partIndex, part] of wrapped.entries()) {
69432
69445
  const buffer = makeBuffer();
69433
- const tokens = tokenizeCodeLine(` ${part}`);
69446
+ const gutter = partIndex === 0 ? gutterPrefix : continuationGutter;
69447
+ appendSegment(buffer, { text: gutter, token: "fgDim", bold: false });
69448
+ const tokens = tokenizeCodeLine(part);
69434
69449
  for (const tk of tokens) {
69435
69450
  appendSegment(buffer, { text: tk.text, token: tk.token, bold: tk.bold });
69436
69451
  }
69437
- const line6 = freezeLine(
69452
+ const codeLine = freezeLine(
69438
69453
  buffer,
69439
69454
  row,
69440
69455
  index2 === 0 && partIndex === 0 ? sourceStart : -1
69441
69456
  );
69442
- out.push({ ...line6, background: "codeBg" });
69457
+ out.push({
69458
+ ...codeLine,
69459
+ background: "codeBg",
69460
+ backgroundColumns: Math.max(width, codeLine.displayWidth)
69461
+ });
69443
69462
  row += 1;
69444
69463
  }
69445
69464
  }
@@ -72510,6 +72529,7 @@ var ScreenAtlas = class {
72510
72529
  snapshotRanges = [];
72511
72530
  snapshotRows = /* @__PURE__ */ new Map();
72512
72531
  snapshotRail;
72532
+ snapshotGeometry;
72513
72533
  /**
72514
72534
  * @param width - terminal columns.
72515
72535
  * @param height - terminal rows.
@@ -72694,6 +72714,7 @@ var ScreenAtlas = class {
72694
72714
  this.snapshotRanges = [];
72695
72715
  this.snapshotRows.clear();
72696
72716
  this.snapshotRail = snapshot.geometry.rail;
72717
+ this.snapshotGeometry = snapshot.geometry;
72697
72718
  const saved = {
72698
72719
  col: this.cursorCol,
72699
72720
  row: this.cursorRow,
@@ -72764,6 +72785,34 @@ var ScreenAtlas = class {
72764
72785
  }
72765
72786
  return this.cells[(row - 1) * this.width + (col - 1)];
72766
72787
  }
72788
+ /** Bounding terminal rows for the transcript content area. */
72789
+ get transcriptBounds() {
72790
+ if (this.snapshotGeometry !== void 0 && this.snapshotGeometry.rows >= 10) {
72791
+ const top = this.snapshotGeometry.transcriptTop;
72792
+ const rows = this.snapshotGeometry.transcriptRows;
72793
+ return { top, bottom: Math.max(top, top + rows - 1) };
72794
+ }
72795
+ return { top: 1, bottom: this.height };
72796
+ }
72797
+ /** Clamp screen point to selectable transcript rows and valid columns. */
72798
+ clampToTranscript(point3) {
72799
+ const { top, bottom } = this.transcriptBounds;
72800
+ return {
72801
+ col: Math.max(1, Math.min(this.width, point3.col)),
72802
+ row: Math.max(top, Math.min(bottom, point3.row))
72803
+ };
72804
+ }
72805
+ /** Plain text for a horizontal slice of one row with rail excluded. */
72806
+ extractRowText(row, fromCol = 1, toCol = this.width) {
72807
+ let line5 = "";
72808
+ for (let col = fromCol; col <= toCol; col++) {
72809
+ if (this.isSnapshotRailControlCell(col, row)) continue;
72810
+ const cell = this.cellAt(col, row);
72811
+ if (cell === void 0 || cell.ch === "") continue;
72812
+ line5 += cell.ch;
72813
+ }
72814
+ return line5.replace(/ +$/u, "");
72815
+ }
72767
72816
  /**
72768
72817
  * Reading-order plain text between two inclusive endpoints. Published rail
72769
72818
  * cells are excluded, trailing spaces are trimmed, and rows join with `\n`.
@@ -72777,14 +72826,7 @@ var ScreenAtlas = class {
72777
72826
  for (let row = start.row; row <= end.row; row++) {
72778
72827
  const from2 = row === start.row ? start.col : 1;
72779
72828
  const to = row === end.row ? end.col : this.width;
72780
- let line5 = "";
72781
- for (let col = from2; col <= to; col++) {
72782
- if (this.isSnapshotRailControlCell(col, row)) continue;
72783
- const cell = this.cellAt(col, row);
72784
- if (cell === void 0 || cell.ch === "") continue;
72785
- line5 += cell.ch;
72786
- }
72787
- lines.push(line5.replace(/ +$/u, ""));
72829
+ lines.push(this.extractRowText(row, from2, to));
72788
72830
  }
72789
72831
  return lines.join("\n");
72790
72832
  }
@@ -73153,6 +73195,9 @@ var MouseSession = class {
73153
73195
  selection;
73154
73196
  paintedSelection;
73155
73197
  railDrag;
73198
+ dragVirtualStartRow = 0;
73199
+ scrolledOffAbove = [];
73200
+ scrolledOffBelow = [];
73156
73201
  edgeTimer;
73157
73202
  openHref;
73158
73203
  copy;
@@ -73180,7 +73225,7 @@ var MouseSession = class {
73180
73225
  */
73181
73226
  handle(event) {
73182
73227
  if (event.kind === "wheel" && event.delta !== void 0) {
73183
- this.onScroll(event.delta);
73228
+ this.scroll(event.delta);
73184
73229
  return "";
73185
73230
  }
73186
73231
  const point3 = { col: event.col, row: event.row };
@@ -73194,8 +73239,12 @@ var MouseSession = class {
73194
73239
  this.onRail(railFraction(region, event.row));
73195
73240
  return this.repaintSelection();
73196
73241
  }
73197
- this.drag = { start: point3, moved: false };
73198
- this.selection = { start: point3, end: point3 };
73242
+ const clampedPoint = this.atlas.clampToTranscript(point3);
73243
+ this.drag = { start: clampedPoint, moved: false };
73244
+ this.dragVirtualStartRow = clampedPoint.row;
73245
+ this.scrolledOffAbove = [];
73246
+ this.scrolledOffBelow = [];
73247
+ this.selection = { start: clampedPoint, end: clampedPoint };
73199
73248
  return "";
73200
73249
  }
73201
73250
  if (event.kind === "drag" && this.railDrag !== void 0 && event.button === "left") {
@@ -73208,27 +73257,52 @@ var MouseSession = class {
73208
73257
  return "";
73209
73258
  }
73210
73259
  if (event.kind === "drag" && this.drag !== void 0 && event.button === "left") {
73211
- if (event.col !== this.drag.start.col || event.row !== this.drag.start.row) {
73260
+ const clampedPoint = this.atlas.clampToTranscript(point3);
73261
+ if (clampedPoint.col !== this.drag.start.col || clampedPoint.row !== this.drag.start.row) {
73212
73262
  this.drag.moved = true;
73213
73263
  }
73214
- this.selection = { start: this.drag.start, end: point3 };
73264
+ const { top, bottom } = this.atlas.transcriptBounds;
73265
+ const visualStartRow = Math.max(top, Math.min(bottom, this.dragVirtualStartRow));
73266
+ this.selection = {
73267
+ start: { col: this.drag.start.col, row: visualStartRow },
73268
+ end: clampedPoint
73269
+ };
73215
73270
  this.syncEdge(event.row);
73216
73271
  return this.repaintSelection();
73217
73272
  }
73218
73273
  if (event.kind === "release" && this.drag !== void 0) {
73219
73274
  this.stopEdge();
73220
- if (event.col !== this.drag.start.col || event.row !== this.drag.start.row) {
73275
+ const clampedPoint = this.atlas.clampToTranscript(point3);
73276
+ if (clampedPoint.col !== this.drag.start.col || clampedPoint.row !== this.drag.start.row) {
73221
73277
  this.drag.moved = true;
73222
- this.selection = { start: this.drag.start, end: point3 };
73223
73278
  }
73279
+ const { top, bottom } = this.atlas.transcriptBounds;
73280
+ const visualStartRow = Math.max(top, Math.min(bottom, this.dragVirtualStartRow));
73281
+ this.selection = {
73282
+ start: { col: this.drag.start.col, row: visualStartRow },
73283
+ end: clampedPoint
73284
+ };
73224
73285
  if (this.drag.moved) {
73225
73286
  const range = this.selection;
73226
- this.copy(this.atlas.extract(range.start, range.end));
73287
+ if (this.scrolledOffAbove.length === 0 && this.scrolledOffBelow.length === 0) {
73288
+ this.copy(this.atlas.extract(range.start, range.end));
73289
+ } else {
73290
+ const visibleText = this.atlas.extract(range.start, range.end);
73291
+ const parts = [];
73292
+ if (this.scrolledOffAbove.length > 0) parts.push(...this.scrolledOffAbove);
73293
+ if (visibleText.length > 0) parts.push(visibleText);
73294
+ if (this.scrolledOffBelow.length > 0) parts.push(...this.scrolledOffBelow);
73295
+ const textToCopy = parts.join("\n");
73296
+ if (textToCopy.length > 0) this.copy(textToCopy);
73297
+ }
73227
73298
  } else {
73228
73299
  const href = this.atlas.urlAt(event.col, event.row);
73229
73300
  if (href !== void 0) this.openHref(href);
73230
73301
  }
73231
73302
  this.drag = void 0;
73303
+ this.dragVirtualStartRow = 0;
73304
+ this.scrolledOffAbove = [];
73305
+ this.scrolledOffBelow = [];
73232
73306
  this.selection = void 0;
73233
73307
  return this.repaintSelection();
73234
73308
  }
@@ -73270,19 +73344,41 @@ var MouseSession = class {
73270
73344
  this.railDrag = void 0;
73271
73345
  }
73272
73346
  syncEdge(row) {
73273
- const top = row <= 2;
73274
- const bottom = row >= this.atlas.height - 1;
73275
- if (!top && !bottom) {
73347
+ if (this.atlas.height < 6) return;
73348
+ const top = 2;
73349
+ const bottom = this.atlas.height - 1;
73350
+ const atTop = row <= top;
73351
+ const atBottom = row >= bottom;
73352
+ if (!atTop && !atBottom) {
73276
73353
  this.stopEdge();
73277
73354
  return;
73278
73355
  }
73279
- const delta = top ? 1 : -1;
73356
+ const delta = atTop ? 1 : -1;
73280
73357
  if (this.edgeTimer !== void 0) return;
73281
- this.onScroll(delta);
73358
+ this.scroll(delta);
73282
73359
  this.edgeTimer = this.startInterval(() => {
73283
- this.onScroll(delta);
73360
+ this.scroll(delta);
73284
73361
  }, 80);
73285
73362
  }
73363
+ scroll(delta) {
73364
+ if (this.drag !== void 0 && this.drag.moved) {
73365
+ const { top, bottom } = this.atlas.transcriptBounds;
73366
+ if (delta > 0) {
73367
+ if (this.dragVirtualStartRow > bottom) {
73368
+ const text4 = this.atlas.extractRowText(bottom);
73369
+ if (text4.length > 0) this.scrolledOffBelow.unshift(text4);
73370
+ }
73371
+ this.dragVirtualStartRow += delta;
73372
+ } else if (delta < 0) {
73373
+ if (this.dragVirtualStartRow < top) {
73374
+ const text4 = this.atlas.extractRowText(top);
73375
+ if (text4.length > 0) this.scrolledOffAbove.push(text4);
73376
+ }
73377
+ this.dragVirtualStartRow += delta;
73378
+ }
73379
+ }
73380
+ this.onScroll(delta);
73381
+ }
73286
73382
  stopEdge() {
73287
73383
  if (this.edgeTimer === void 0) return;
73288
73384
  this.stopInterval(this.edgeTimer);
@@ -79119,7 +79215,7 @@ function mapKeyEvent(state, key, keyInfo, commands, pane, search2, timeline = {
79119
79215
  renaming: state.renaming
79120
79216
  };
79121
79217
  }
79122
- if (commandMode && (keyInfo.downArrow || keyInfo.upArrow)) {
79218
+ if (commandMode && state.historyIndex === void 0 && (keyInfo.downArrow || keyInfo.upArrow)) {
79123
79219
  const query = state.commandQuery ?? state.text.slice(1);
79124
79220
  const count = filterCommands(commands, query).length;
79125
79221
  const delta = keyInfo.downArrow ? 1 : -1;
@@ -79397,6 +79493,7 @@ function mapKeyEvent(state, key, keyInfo, commands, pane, search2, timeline = {
79397
79493
  commandQuery: void 0,
79398
79494
  prefixG: false,
79399
79495
  renaming: state.renaming,
79496
+ commandDismissed: true,
79400
79497
  caretIndex: nextText.length,
79401
79498
  historyIndex: nextIndex,
79402
79499
  historyDraft: savedDraft
@@ -79423,6 +79520,7 @@ function mapKeyEvent(state, key, keyInfo, commands, pane, search2, timeline = {
79423
79520
  commandQuery: void 0,
79424
79521
  prefixG: false,
79425
79522
  renaming: state.renaming,
79523
+ commandDismissed: true,
79426
79524
  caretIndex: nextText.length,
79427
79525
  historyIndex: nextIndex,
79428
79526
  historyDraft: state.historyDraft
@@ -79796,18 +79894,18 @@ function TuiLoop({
79796
79894
  helpLinesRef.current = helpPane.lines;
79797
79895
  const historyLengthRef = (0, import_react39.useRef)(model.history.length);
79798
79896
  historyLengthRef.current = model.history.length;
79799
- const sentHistoryRef = (0, import_react39.useRef)([]);
79897
+ const [sentHistory, setSentHistory] = (0, import_react39.useState)([]);
79800
79898
  const inputHistory = (0, import_react39.useMemo)(() => {
79801
79899
  const fromModel = model.history.filter((m) => m.kind === "user" && typeof m.text === "string" && m.text.trim().length > 0).map((m) => m.text);
79802
79900
  const combined = [];
79803
- for (const item of [...fromModel, ...sentHistoryRef.current]) {
79901
+ for (const item of [...fromModel, ...sentHistory]) {
79804
79902
  const trimmed = item.trim();
79805
79903
  if (trimmed.length > 0 && combined.at(-1) !== trimmed) {
79806
79904
  combined.push(trimmed);
79807
79905
  }
79808
79906
  }
79809
79907
  return combined;
79810
- }, [model.history]);
79908
+ }, [model.history, sentHistory]);
79811
79909
  const chordTimerRef = (0, import_react39.useRef)(
79812
79910
  void 0
79813
79911
  );
@@ -79961,19 +80059,19 @@ function TuiLoop({
79961
80059
  issueViewportCommand({ kind: "edge", edge: action.edge });
79962
80060
  } else if (action.kind === "send") {
79963
80061
  const trimmed = action.text.trim();
79964
- if (trimmed.length > 0 && sentHistoryRef.current.at(-1) !== trimmed) {
79965
- sentHistoryRef.current.push(trimmed);
80062
+ if (trimmed.length > 0) {
80063
+ setSentHistory((prev) => prev.at(-1) === trimmed ? prev : [...prev, trimmed]);
79966
80064
  }
79967
80065
  issueViewportCommand({ kind: "reset" });
79968
80066
  controller.dispatch(action);
79969
80067
  } else if (action.kind === "command") {
79970
80068
  const queryText = action.query.trim().startsWith("/") ? action.query.trim() : `/${action.query.trim()}`;
79971
- if (queryText.length > 0 && sentHistoryRef.current.at(-1) !== queryText) {
79972
- sentHistoryRef.current.push(queryText);
80069
+ if (queryText.length > 0) {
80070
+ setSentHistory((prev) => prev.at(-1) === queryText ? prev : [...prev, queryText]);
79973
80071
  }
79974
80072
  controller.dispatch(action);
79975
80073
  } else if (action.kind === "new-session" || action.kind === "select-session") {
79976
- sentHistoryRef.current = [];
80074
+ setSentHistory([]);
79977
80075
  issueViewportCommand({ kind: "reset" });
79978
80076
  controller.dispatch(action);
79979
80077
  } else if (action.kind === "intake-clipboard-image") {
@@ -80141,7 +80239,13 @@ function TuiLoop({
80141
80239
  let status;
80142
80240
  let statusRowCount;
80143
80241
  if (feedback !== void 0) {
80144
- statusRowCount = 1;
80242
+ const rawLines = feedback.split("\n");
80243
+ const maxBudget = Math.max(1, Math.min(rawLines.length, Math.max(4, Math.floor((rows - 8) / 2))));
80244
+ const visibleLines = rawLines.slice(0, maxBudget);
80245
+ if (rawLines.length > maxBudget) {
80246
+ visibleLines[visibleLines.length - 1] = `${visibleLines[visibleLines.length - 1]} \u2026 (+${rawLines.length - maxBudget} \u884C)`;
80247
+ }
80248
+ statusRowCount = visibleLines.length;
80145
80249
  status = (0, import_react39.createElement)(
80146
80250
  Box_default,
80147
80251
  {
@@ -80149,11 +80253,14 @@ function TuiLoop({
80149
80253
  width: "100%",
80150
80254
  backgroundColor: inkColor("bg")
80151
80255
  },
80152
- (0, import_react39.createElement)(
80153
- Text,
80154
- null,
80155
- paintBackgroundRow([feedbackLine(feedback)], "bg", columns)
80156
- )
80256
+ ...visibleLines.map((line5, index2) => {
80257
+ const painted = index2 === 0 ? feedbackLine(line5) : paintRow([styled(escapeContent(line5), line5.includes("*") || line5.includes("\u6D3B\u8DC3") ? "accentText" : "fg")]);
80258
+ return (0, import_react39.createElement)(
80259
+ Text,
80260
+ { key: index2 },
80261
+ paintBackgroundRow([painted], "bg", columns)
80262
+ );
80263
+ })
80157
80264
  );
80158
80265
  } else if (adaptiveRows.length > 0) {
80159
80266
  const goalLine = goalRuns === void 0 ? void 0 : `${goalRuns.head} \xB7 ${goalRuns.objective}`;
@@ -82205,6 +82312,7 @@ var LOCAL_COMMANDS = [
82205
82312
  { name: "key", description: "Set or update API key", input: { hint: "[KEY_NAME] <SECRET>" } },
82206
82313
  { name: "login", description: "Set or update API key", input: { hint: "[KEY_NAME] <SECRET>" } },
82207
82314
  { name: "model", description: "Switch the active model" },
82315
+ { name: "new", description: "Start a new session" },
82208
82316
  { name: "reload", description: "Relaunch this process and resume the session" },
82209
82317
  { name: "resume", description: "Switch or resume a session" },
82210
82318
  { name: "settings", description: "Edit settings, catalogs, and General" }
@@ -82437,6 +82545,7 @@ var RuntimeController = class _RuntimeController {
82437
82545
  modelOpen = false;
82438
82546
  modelFilter = "";
82439
82547
  modelRows = [];
82548
+ cachedModelRows;
82440
82549
  modelSelectedIndex = 0;
82441
82550
  modelStatus = "idle";
82442
82551
  modelError;
@@ -84418,7 +84527,8 @@ var RuntimeController = class _RuntimeController {
84418
84527
  turnEndReason: last.kind,
84419
84528
  ...last.abortCause === void 0 ? {} : { abortCause: last.abortCause },
84420
84529
  ...last.errorText === void 0 ? {} : { errorText: last.errorText },
84421
- sessionTitle: this.notifySessionTitle()
84530
+ sessionTitle: this.notifySessionTitle(),
84531
+ currentPrompt: this.currentPromptText()
84422
84532
  }));
84423
84533
  }
84424
84534
  /**
@@ -84433,7 +84543,8 @@ var RuntimeController = class _RuntimeController {
84433
84543
  secondsSinceLastInput: this.secondsSinceLastInput(),
84434
84544
  toolName: input.toolName,
84435
84545
  questionText: input.questionText,
84436
- sessionTitle: this.notifySessionTitle()
84546
+ sessionTitle: this.notifySessionTitle(),
84547
+ currentPrompt: this.currentPromptText()
84437
84548
  }));
84438
84549
  }
84439
84550
  /**
@@ -84483,6 +84594,15 @@ var RuntimeController = class _RuntimeController {
84483
84594
  notifySessionTitle() {
84484
84595
  return this.session === void 0 ? void 0 : notifySessionTitle(getSessionEvents(this.session));
84485
84596
  }
84597
+ currentPromptText() {
84598
+ if (this.session === void 0) return void 0;
84599
+ const events = getSessionEvents(this.session);
84600
+ if (!Array.isArray(events)) return void 0;
84601
+ const lastUser = events.findLast(isHumanUserMessage);
84602
+ if (lastUser === void 0) return void 0;
84603
+ const text4 = joinTextBlocks(lastUser.data.content, " ").trim();
84604
+ return text4.length > 0 ? text4 : void 0;
84605
+ }
84486
84606
  /** Record local user input for the notification quiet window. */
84487
84607
  noteUserActivity() {
84488
84608
  this.lastInputAt = Date.now();
@@ -85271,6 +85391,12 @@ var RuntimeController = class _RuntimeController {
85271
85391
  this.ownWork(this.exportLive(), "session export");
85272
85392
  return;
85273
85393
  }
85394
+ if (query === "new" || query === "new ") {
85395
+ if (this.blockingHead() !== void 0) return;
85396
+ this.closeOtherPanels();
85397
+ this.dispatch({ kind: "new-session" });
85398
+ return;
85399
+ }
85274
85400
  if (query === "reload") {
85275
85401
  this.requestReload();
85276
85402
  return;
@@ -85351,7 +85477,7 @@ var RuntimeController = class _RuntimeController {
85351
85477
  return;
85352
85478
  }
85353
85479
  if (execution.result.kind !== "success") {
85354
- this.reportCommandFailure(firstLine2(execution.result.text));
85480
+ this.reportCommandFailure(execution.result.text.trim());
85355
85481
  return;
85356
85482
  }
85357
85483
  const text4 = execution.result.text;
@@ -85371,7 +85497,7 @@ var RuntimeController = class _RuntimeController {
85371
85497
  if (wasPermission || wasPlanDirectory) this.emit();
85372
85498
  return;
85373
85499
  }
85374
- this.setFeedback(`\u2713 ${firstLine2(text4)}`);
85500
+ this.setFeedback(`\u2713 ${text4.trim()}`);
85375
85501
  },
85376
85502
  (error51) => {
85377
85503
  if (seq !== this.commandSeq) return;
@@ -86030,7 +86156,7 @@ var RuntimeController = class _RuntimeController {
86030
86156
  "\u2191\u2193/jk \u6EDA\u52A8 \xB7 g s \u4F1A\u8BDD\u5217\u8868 \xB7 Tab \u8865\u5168 \xB7 Esc \u5173\u95ED \xB7 / \u547D\u4EE4 \xB7 @ \u63D0\u53CA",
86031
86157
  "\u6EDA\u8F6E\u6EDA\u52A8 \xB7 \u70B9\u51FB\u6253\u5F00\u94FE\u63A5 \xB7 \u62D6\u9009\u590D\u5236",
86032
86158
  "/plan \u8BA1\u5212 \xB7 /goal \u76EE\u6807 \xB7 /compact \u538B\u7F29",
86033
- "/model \u6A21\u578B\u9009\u62E9 \xB7 /help \u5E2E\u52A9 \xB7 /export \u5BFC\u51FA\u4F1A\u8BDD \xB7 /settings \u8BBE\u7F6E \xB7 /resume \u4F1A\u8BDD \xB7 /reload \u91CD\u8F7D"
86159
+ "/model \u6A21\u578B\u9009\u62E9 \xB7 /new \u65B0\u4F1A\u8BDD \xB7 /help \u5E2E\u52A9 \xB7 /export \u5BFC\u51FA\u4F1A\u8BDD \xB7 /settings \u8BBE\u7F6E \xB7 /resume \u4F1A\u8BDD \xB7 /reload \u91CD\u8F7D"
86034
86160
  ];
86035
86161
  }
86036
86162
  /**
@@ -86048,11 +86174,21 @@ var RuntimeController = class _RuntimeController {
86048
86174
  * the panel transients, then load the provider/model catalog concurrently.
86049
86175
  */
86050
86176
  openModelPane() {
86051
- this.resetModelState();
86052
86177
  this.closeOtherPanels();
86178
+ this.modelFilter = "";
86179
+ this.modelSelectedIndex = 0;
86053
86180
  this.modelOpen = true;
86181
+ if (this.cachedModelRows !== void 0 && this.cachedModelRows.length > 0) {
86182
+ this.modelRows = [...this.cachedModelRows];
86183
+ this.modelStatus = "idle";
86184
+ this.emit();
86185
+ this.ownWork(this.loadModelCatalog(true), "model catalog background refresh");
86186
+ return;
86187
+ }
86188
+ this.modelRows = [];
86189
+ this.modelStatus = "loading";
86054
86190
  this.emit();
86055
- this.ownWork(this.loadModelCatalog(), "model catalog load");
86191
+ this.ownWork(this.loadModelCatalog(false), "model catalog load");
86056
86192
  }
86057
86193
  /**
86058
86194
  * The catalog rows narrowed by the live filter: case-insensitive substring
@@ -86074,18 +86210,22 @@ var RuntimeController = class _RuntimeController {
86074
86210
  * degrades to a single `provider 当前默认` row carrying the live default
86075
86211
  * model when the provider is the current one.
86076
86212
  */
86077
- async loadModelCatalog() {
86213
+ async loadModelCatalog(isBackground = false) {
86078
86214
  const seq = ++this.modelSeq;
86079
86215
  const llm = this.ctx.get("llm");
86080
86216
  const defaultModel = this.ctx.get("agentDefaultModel");
86081
86217
  if (llm === void 0 || defaultModel === void 0) {
86082
- this.modelStatus = "error";
86083
- this.modelError = "\u6A21\u578B\u670D\u52A1\u4E0D\u53EF\u7528";
86084
- this.emit();
86218
+ if (!isBackground) {
86219
+ this.modelStatus = "error";
86220
+ this.modelError = "\u6A21\u578B\u670D\u52A1\u4E0D\u53EF\u7528";
86221
+ this.emit();
86222
+ }
86085
86223
  return;
86086
86224
  }
86087
- this.modelStatus = "loading";
86088
- this.emit();
86225
+ if (!isBackground) {
86226
+ this.modelStatus = "loading";
86227
+ this.emit();
86228
+ }
86089
86229
  const current = defaultModel.currentSelection();
86090
86230
  try {
86091
86231
  const providers = llm.listProviders();
@@ -86107,7 +86247,9 @@ var RuntimeController = class _RuntimeController {
86107
86247
  );
86108
86248
  }));
86109
86249
  if (seq !== this.modelSeq) return;
86110
- this.modelRows = groups.flat();
86250
+ const rows = groups.flat();
86251
+ this.cachedModelRows = rows;
86252
+ this.modelRows = [...rows];
86111
86253
  if (this.modelSelectedIndex >= this.modelRows.length) {
86112
86254
  this.modelSelectedIndex = Math.max(0, this.modelRows.length - 1);
86113
86255
  }
@@ -86116,10 +86258,12 @@ var RuntimeController = class _RuntimeController {
86116
86258
  } catch (error51) {
86117
86259
  if (seq !== this.modelSeq) return;
86118
86260
  this.ctx.logger.warn(`model catalog load failed: ${String(error51)}`);
86119
- this.modelRows = [];
86120
- this.modelStatus = "error";
86121
- this.modelError = error51 instanceof Error ? error51.message : String(error51);
86122
- this.emit();
86261
+ if (!isBackground) {
86262
+ this.modelRows = [];
86263
+ this.modelStatus = "error";
86264
+ this.modelError = error51 instanceof Error ? error51.message : String(error51);
86265
+ this.emit();
86266
+ }
86123
86267
  }
86124
86268
  }
86125
86269
  /** Fold one advertised model into a catalog row. */
@@ -86464,7 +86608,7 @@ var RuntimeController = class _RuntimeController {
86464
86608
  isEmpty: isSessionEmpty(events)
86465
86609
  };
86466
86610
  } catch {
86467
- return { id, title: id, updatedAt: 0, isEmpty: false };
86611
+ return { id, title: id, updatedAt: 0, isEmpty: true };
86468
86612
  }
86469
86613
  }
86470
86614
  /** Queue the newest query and enter loading before its debounce delay. */
@@ -86935,9 +87079,16 @@ function foldTitle(events) {
86935
87079
  if (!Array.isArray(events)) return void 0;
86936
87080
  const folded = foldSessionTitle(events);
86937
87081
  if (folded !== void 0) return folded.title;
86938
- const firstUser = events.find(isHumanUserMessage);
86939
- if (firstUser !== void 0) {
86940
- const text4 = joinTextBlocks(firstUser.data.content, " ");
87082
+ const userMessages = events.filter(isHumanUserMessage);
87083
+ if (userMessages.length > 0) {
87084
+ const nonGreeting = userMessages.find((m) => {
87085
+ const text5 = joinTextBlocks(m.data.content, " ").trim().toLowerCase();
87086
+ return !["\u4F60\u597D", "hello", "hi", "hey", "\u60A8\u597D", "test", "say hi"].includes(text5) && text5.length > 2;
87087
+ });
87088
+ const firstUser = userMessages[0];
87089
+ if (firstUser === void 0) return void 0;
87090
+ const chosenUser = nonGreeting ?? firstUser;
87091
+ const text4 = joinTextBlocks(chosenUser.data.content, " ");
86941
87092
  const fallback = fallbackSessionTitle(
86942
87093
  text4,
86943
87094
  LIST_TITLE_MAX_WORDS,
@@ -86967,10 +87118,6 @@ function isSessionEmpty(events) {
86967
87118
  if (isBootstrapOnlySession(events)) return true;
86968
87119
  return !events.some(isHumanUserMessage) && foldTitle(events) === void 0;
86969
87120
  }
86970
- function firstLine2(text4) {
86971
- const line5 = text4.split("\n", 1)[0];
86972
- return line5.trimEnd();
86973
- }
86974
87121
  function planReviewOf(questions) {
86975
87122
  if (questions.length !== 1) return void 0;
86976
87123
  const question = questions[0];
package/dist/zen-proxy.js CHANGED
@@ -814,6 +814,7 @@ defineMethod("transform", [
814
814
  ], ({ inner }, isInner) => inner.toString(isInner));
815
815
 
816
816
  // ../dsh-tui/plugins/zen-proxy/index.js
817
+ import crypto from "node:crypto";
817
818
  import http from "node:http";
818
819
  import https from "node:https";
819
820
  var name = "zen-proxy";
@@ -822,21 +823,30 @@ var Config = Schema.object({
822
823
  port: Schema.number().default(4097),
823
824
  upstreamHost: Schema.string().default("opencode.ai"),
824
825
  upstreamBasePath: Schema.string().default("/zen/v1"),
825
- userAgent: Schema.string().default("opencode/1.15.5 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14"),
826
+ userAgent: Schema.string().default("opencode/1.18.30"),
826
827
  clientHeader: Schema.string().default("cli"),
827
828
  projectHeader: Schema.string().default("global")
828
829
  });
829
830
  var inject = [];
830
- var rnd = (p) => `${p}${Math.random().toString(36).slice(2, 12)}`;
831
+ var rnd = (p) => `${p}${crypto.randomBytes(13).toString("hex")}`;
831
832
  function apply(ctx, config) {
832
833
  const server = http.createServer((req, res) => {
833
834
  const { method, url } = req;
834
- if (method === "GET" && url === "/v1/models") {
835
+ const parsed = new URL(url, "http://127.0.0.1");
836
+ const pathname = parsed.pathname.replace(/^(\/v1)+/, "/v1").replace(/\/+$/, "") || "/";
837
+ if (method === "GET" && (pathname === "/v1/models" || pathname === "/models")) {
835
838
  forward(req, res, { method: "GET", path: `${config.upstreamBasePath}/models`, body: null });
836
839
  return;
837
840
  }
838
- const forwardablePost = url === "/v1/chat/completions" || url === "/v1/responses";
839
- if (method !== "POST" || !forwardablePost) {
841
+ let targetRoute = null;
842
+ if (pathname === "/v1/chat/completions" || pathname === "/chat/completions") {
843
+ targetRoute = "/chat/completions";
844
+ } else if (pathname === "/v1/responses" || pathname === "/responses") {
845
+ targetRoute = "/responses";
846
+ } else if (pathname === "/v1/messages" || pathname === "/messages") {
847
+ targetRoute = "/messages";
848
+ }
849
+ if (method !== "POST" || !targetRoute) {
840
850
  res.writeHead(404, { "content-type": "application/json" });
841
851
  res.end(JSON.stringify({ error: { type: "not_found", message: `zen-proxy: unsupported ${method} ${url}` } }));
842
852
  return;
@@ -846,8 +856,7 @@ function apply(ctx, config) {
846
856
  body += c;
847
857
  });
848
858
  req.on("end", () => {
849
- const upstreamPath = url === "/v1/responses" ? "/responses" : "/chat/completions";
850
- forward(req, res, { method: "POST", path: `${config.upstreamBasePath}${upstreamPath}`, body });
859
+ forward(req, res, { method: "POST", path: `${config.upstreamBasePath}${targetRoute}`, body });
851
860
  });
852
861
  });
853
862
  server.on("error", (error) => {
@@ -864,17 +873,27 @@ function apply(ctx, config) {
864
873
  };
865
874
  }, "zen-proxy.server");
866
875
  function forward(req, res, { method, path, body }) {
876
+ const sessionId = rnd("ses_");
867
877
  const headers = {
868
878
  "content-type": "application/json",
869
879
  "user-agent": config.userAgent,
870
880
  "x-opencode-client": config.clientHeader,
871
881
  "x-opencode-project": config.projectHeader,
872
- "x-opencode-session": rnd("ses_"),
882
+ "x-opencode-session": sessionId,
883
+ "x-session-id": sessionId,
873
884
  "x-opencode-request": rnd("msg_")
874
885
  };
875
886
  if (req.headers.authorization !== void 0) {
876
887
  headers.authorization = req.headers.authorization;
877
888
  }
889
+ if (req.headers["anthropic-version"] !== void 0) {
890
+ headers["anthropic-version"] = req.headers["anthropic-version"];
891
+ } else if (path.endsWith("/messages")) {
892
+ headers["anthropic-version"] = "2023-06-01";
893
+ }
894
+ if (req.headers["anthropic-beta"] !== void 0) {
895
+ headers["anthropic-beta"] = req.headers["anthropic-beta"];
896
+ }
878
897
  if (body !== null) {
879
898
  headers["content-length"] = Buffer.byteLength(body);
880
899
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crazyhappyone/dsh-tui",
3
- "version": "0.1.0-alpha.43",
3
+ "version": "0.1.0-alpha.44",
4
4
  "description": "Source-runtime launcher for the DeepSeek Harness terminal interface",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  "LICENSE"
20
20
  ],
21
21
  "scripts": {
22
- "test": "node --test",
22
+ "test": "node --test tests/*.test.js",
23
23
  "pack:check": "npm pack --dry-run --json"
24
24
  },
25
25
  "engines": {
@@ -76,6 +76,16 @@
76
76
  # pi-lens-ignore: YAML:0
77
77
  model: !!js "process.env.DSH_MODEL ?? 'deepseek-v4-flash'"
78
78
 
79
+ # pi-lens-ignore: YAML:0
80
+ - id: session-title-llm
81
+ name: "@deepseek-ai/dsh-session-title-all-prompts-llm"
82
+ config:
83
+ targetWords: 5
84
+ targetCjkCharacters: 10
85
+ maxInputBytes: 16384
86
+ maxOutputTokens: 64
87
+ timeoutMs: 60000
88
+
79
89
  - insert:
80
90
  # Code Mode is a core execution capability, not a Web component.
81
91
  - id: code-runtime