@crazyhappyone/dsh-tui 0.1.0-alpha.45 → 0.1.0-alpha.47

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
@@ -28740,6 +28740,71 @@ function exitStatus(code2, signal) {
28740
28740
  return code2 ?? 1;
28741
28741
  }
28742
28742
 
28743
+ // packages/tui/tui/src/session-errors.ts
28744
+ var SESSION_WRITER_HELD_COPY = "\u5F53\u524D\u4F1A\u8BDD\u5DF2\u88AB\u5360\u7528\uFF0C\u53EF\u80FD\u662F\u5176\u4ED6\u6B63\u5728\u8FD0\u884C\u7684 DSH \u5BFC\u81F4\u7684\uFF08\u5982\u5176\u4ED6 dsh web\u3001\u684C\u9762\u7AEF\uFF09\uFF0C\u8BF7\u9000\u51FA\u5176\u4ED6\u6B63\u5728\u8FD0\u884C\u7684 DSH \u540E\u91CD\u8BD5\u3002";
28745
+ var SESSION_CHANGES_DISPOSED_COPY = "\u4F1A\u8BDD\u5DF2\u91CA\u653E\uFF0C\u65E0\u6CD5\u5BF9\u6BD4\u8BE5\u6587\u4EF6";
28746
+ function isRecord(value) {
28747
+ return typeof value === "object" && value !== null;
28748
+ }
28749
+ function isSessionWriterHeld(error51) {
28750
+ if (error51 instanceof AggregateError) {
28751
+ return error51.errors.some(isSessionWriterHeld);
28752
+ }
28753
+ if (!isRecord(error51)) return false;
28754
+ if (error51.name === "SessionAlreadyOwnedError") return true;
28755
+ if (error51.code === "session/writer-held") return true;
28756
+ if (error51.cause !== void 0 && isSessionWriterHeld(error51.cause)) return true;
28757
+ return false;
28758
+ }
28759
+ function userErrorReason(error51) {
28760
+ if (isSessionWriterHeld(error51)) return SESSION_WRITER_HELD_COPY;
28761
+ return error51 instanceof Error ? error51.message : String(error51);
28762
+ }
28763
+
28764
+ // packages/tui/tui/src/workspace-changes.ts
28765
+ function workspaceChangesCardFromSummary(seq, summary) {
28766
+ if (summary === void 0 || summary.files.length === 0) return void 0;
28767
+ return {
28768
+ seq,
28769
+ turn: summary.turn,
28770
+ files: summary.files.map(toFileView),
28771
+ total: summary.total,
28772
+ added: summary.added,
28773
+ deleted: summary.deleted
28774
+ };
28775
+ }
28776
+ function toFileView(file2) {
28777
+ return {
28778
+ path: file2.path,
28779
+ display: file2.display,
28780
+ added: file2.added,
28781
+ deleted: file2.deleted,
28782
+ ...file2.binary === true ? { binary: true } : {},
28783
+ ...file2.oversized === true ? { oversized: true } : {}
28784
+ };
28785
+ }
28786
+ function lookupWorkspaceChange(cards, needle) {
28787
+ for (const card of cards.values()) {
28788
+ const index2 = card.files.findIndex((file2) => file2.path === needle || file2.display === needle);
28789
+ if (index2 >= 0) return { seq: card.seq, index: index2 };
28790
+ }
28791
+ return void 0;
28792
+ }
28793
+ function formatWorkspaceChangeDiffPreview(diff2) {
28794
+ if (diff2 === void 0) return void 0;
28795
+ if (diff2.kind === "binary" || diff2.kind === "oversized") {
28796
+ return [`${diff2.display} \xB7 ${diff2.kind}`];
28797
+ }
28798
+ const lines = [
28799
+ `${diff2.display}${diff2.coarse ? " \xB7 coarse" : ""}`
28800
+ ];
28801
+ for (const hunk of diff2.hunks) {
28802
+ lines.push(`@@ -${String(hunk.oldStart)},${String(hunk.oldLines)} +${String(hunk.newStart)},${String(hunk.newLines)} @@`);
28803
+ lines.push(...hunk.lines);
28804
+ }
28805
+ return lines;
28806
+ }
28807
+
28743
28808
  // packages/tui/tui/src/external-editor.ts
28744
28809
  import {
28745
28810
  chmod,
@@ -30458,8 +30523,9 @@ var LoggerService = class _LoggerService {
30458
30523
  */
30459
30524
  exporter(exporter) {
30460
30525
  return this.ctx.effect(() => {
30461
- this.exporters.set(++this._snExporter, exporter);
30462
- return () => this.exporters.delete(this._snExporter);
30526
+ const id = ++this._snExporter;
30527
+ this.exporters.set(id, exporter);
30528
+ return () => this.exporters.delete(id);
30463
30529
  }, "ctx.logger.exporter()");
30464
30530
  }
30465
30531
  _resolveConfig() {
@@ -31027,8 +31093,8 @@ var Fiber = class {
31027
31093
  *
31028
31094
  * @param config — the new raw config; validated before anything restarts.
31029
31095
  * @param noSave — hint for persistence hooks not to write the change back.
31030
- * @returns the update waterfall result; the default restart returns a promise.
31031
- * @throws when validation, an update listener, or the restarted plugin fails.
31096
+ * @returns nothing; the restart runs behind the `internal/update` waterfall.
31097
+ * @throws {ValidationError} when the new config fails validation.
31032
31098
  */
31033
31099
  update(config2, noSave = false) {
31034
31100
  this.assertActive();
@@ -31040,7 +31106,7 @@ var Fiber = class {
31040
31106
  return;
31041
31107
  }
31042
31108
  config2 = this._resolveConfig(config2);
31043
- return this.context.waterfall(this, "internal/update", config2, noSave, () => {
31109
+ this.context.waterfall(this, "internal/update", config2, noSave, () => {
31044
31110
  this.config = config2;
31045
31111
  this._error = void 0;
31046
31112
  return this.restart();
@@ -32201,13 +32267,15 @@ function failureSnapshot(value) {
32201
32267
  const status = candidate.status;
32202
32268
  const providerRetryAfterMs = candidate.providerRetryAfterMs;
32203
32269
  const requestId = candidate.requestId;
32204
- if (typeof message !== "string" || message.length === 0 || typeof code2 !== "string" || code2.length === 0 || status !== void 0 && (!Number.isInteger(status) || status < 100 || status > 599) || providerRetryAfterMs !== void 0 && (!Number.isFinite(providerRetryAfterMs) || providerRetryAfterMs <= 0) || requestId !== void 0 && (typeof requestId !== "string" || requestId.length === 0)) return void 0;
32270
+ const offloadImages = candidate.offloadImages;
32271
+ if (typeof message !== "string" || message.length === 0 || typeof code2 !== "string" || code2.length === 0 || status !== void 0 && (!Number.isInteger(status) || status < 100 || status > 599) || providerRetryAfterMs !== void 0 && (!Number.isFinite(providerRetryAfterMs) || providerRetryAfterMs <= 0) || requestId !== void 0 && (typeof requestId !== "string" || requestId.length === 0) || offloadImages !== void 0 && (!Number.isSafeInteger(offloadImages) || offloadImages <= 0)) return void 0;
32205
32272
  return Object.freeze({
32206
32273
  message,
32207
32274
  code: code2,
32208
32275
  ...status === void 0 ? {} : { status },
32209
32276
  ...providerRetryAfterMs === void 0 ? {} : { providerRetryAfterMs },
32210
- ...requestId === void 0 ? {} : { requestId }
32277
+ ...requestId === void 0 ? {} : { requestId },
32278
+ ...offloadImages === void 0 ? {} : { offloadImages }
32211
32279
  });
32212
32280
  } catch (_sdkFailureGetter) {
32213
32281
  return void 0;
@@ -32508,7 +32576,8 @@ var LlmError = class extends HarnessError {
32508
32576
  code: code2,
32509
32577
  ...options?.status === void 0 ? {} : { status: options.status },
32510
32578
  ...options?.providerRetryAfterMs === void 0 ? {} : { providerRetryAfterMs: options.providerRetryAfterMs },
32511
- ...options?.requestId === void 0 ? {} : { requestId: options.requestId }
32579
+ ...options?.requestId === void 0 ? {} : { requestId: options.requestId },
32580
+ ...options?.offloadImages === void 0 ? {} : { offloadImages: options.offloadImages }
32512
32581
  });
32513
32582
  }
32514
32583
  };
@@ -32731,7 +32800,8 @@ var LlmRuntime = class extends (_a = TypertRemoteService, _listProviders_dec = [
32731
32800
  id: model.id,
32732
32801
  ...model.name === void 0 ? {} : { name: model.name },
32733
32802
  ...model.contextWindow === void 0 ? {} : { contextWindow: model.contextWindow },
32734
- ...model.maxTokens === void 0 ? {} : { maxTokens: model.maxTokens }
32803
+ ...model.maxTokens === void 0 ? {} : { maxTokens: model.maxTokens },
32804
+ ...model.inputModalities === void 0 ? {} : { inputModalities: [...model.inputModalities] }
32735
32805
  });
32736
32806
  }
32737
32807
  return models;
@@ -33274,6 +33344,7 @@ var KNOWN_SESSION_EVENT_TYPES = /* @__PURE__ */ new Set([
33274
33344
  "goal/change",
33275
33345
  "hook/invoked",
33276
33346
  "hook/result",
33347
+ "image/offload",
33277
33348
  "llm/retry",
33278
33349
  "llm/retry-started",
33279
33350
  "model/selection",
@@ -33309,7 +33380,11 @@ var KNOWN_SESSION_EVENT_TYPES = /* @__PURE__ */ new Set([
33309
33380
  "turn/end",
33310
33381
  "turn/start",
33311
33382
  "user/message",
33312
- "web/deepseek-search-llm-request"
33383
+ "web/deepseek-search-llm-request",
33384
+ "workspace/changes"
33385
+ ]);
33386
+ var MESSAGE_PROJECTION_EVENT_TYPES = /* @__PURE__ */ new Set([
33387
+ "image/offload"
33313
33388
  ]);
33314
33389
 
33315
33390
  // packages/core/session/src/surface.ts
@@ -33322,7 +33397,9 @@ var SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
33322
33397
  function isSurfaceEligibleType(type) {
33323
33398
  return SURFACE_EVENT_TYPES.has(type);
33324
33399
  }
33325
- function deriveEventMessage(event) {
33400
+ function deriveEventMessage(event, projectedMessages) {
33401
+ const projected = projectedMessages?.get(event.seq);
33402
+ if (projected !== void 0) return projected;
33326
33403
  switch (event.type) {
33327
33404
  // Ordinary prompts and injected context project in user role: the event's
33328
33405
  // model-facing content stays verbatim. Do NOT re-add per-type framing
@@ -33352,36 +33429,36 @@ function deriveEventMessage(event) {
33352
33429
  return null;
33353
33430
  }
33354
33431
  }
33355
- function isRecord(value) {
33432
+ function isRecord2(value) {
33356
33433
  return typeof value === "object" && value !== null && !Array.isArray(value);
33357
33434
  }
33358
33435
  function validateSessionEventData(event, subject) {
33359
33436
  const data = event.data;
33360
33437
  if (event.type === "request/header") {
33361
- if (!isRecord(data)) throw new Error(`${subject} data must be an object`);
33438
+ if (!isRecord2(data)) throw new Error(`${subject} data must be an object`);
33362
33439
  const header = data["header"];
33363
- if (!isRecord(header)) throw new Error(`${subject} header must be an object`);
33440
+ if (!isRecord2(header)) throw new Error(`${subject} header must be an object`);
33364
33441
  if (Object.hasOwn(header, "system")) throw new Error(`${subject} must omit header.system; use system/message`);
33365
33442
  if (Array.isArray(header["tools"]) && header["tools"].length === 0) {
33366
33443
  throw new Error(`${subject} must omit empty tools`);
33367
33444
  }
33368
33445
  const defaults = header["adapterDefaults"];
33369
- if (isRecord(defaults) && Object.keys(defaults).length === 0) {
33446
+ if (isRecord2(defaults) && Object.keys(defaults).length === 0) {
33370
33447
  throw new Error(`${subject} must omit empty adapterDefaults`);
33371
33448
  }
33372
33449
  } else if (event.type === "tool/result") {
33373
- if (!isRecord(data)) throw new Error(`${subject} data must be an object`);
33450
+ if (!isRecord2(data)) throw new Error(`${subject} data must be an object`);
33374
33451
  if (data["error"] === void 0) return;
33375
33452
  const message = data["message"];
33376
- const content3 = isRecord(message) ? message["content"] : void 0;
33453
+ const content3 = isRecord2(message) ? message["content"] : void 0;
33377
33454
  const block = Array.isArray(content3) ? content3[0] : void 0;
33378
- if (!isRecord(block) || block["isError"] !== true) {
33455
+ if (!isRecord2(block) || block["isError"] !== true) {
33379
33456
  throw new Error(`${subject} error requires message content[0].isError === true`);
33380
33457
  }
33381
33458
  }
33382
33459
  }
33383
33460
  function createFoldState() {
33384
- return { nodes: [], replaceGeneration: 0 };
33461
+ return { nodes: [], replaceGeneration: 0, contentGeneration: 0, projectedMessages: /* @__PURE__ */ new Map(), projections: /* @__PURE__ */ new Set() };
33385
33462
  }
33386
33463
  function isEventSeq(value) {
33387
33464
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && !Object.is(value, -0);
@@ -33415,7 +33492,7 @@ function surfaceOpOf(event) {
33415
33492
  }
33416
33493
  return op;
33417
33494
  }
33418
- function assertProvenance(event, shadowedSeqs) {
33495
+ function assertSourceEventReferences(event, shadowedSeqs) {
33419
33496
  const raw = event.sourceEventSeqs;
33420
33497
  if (event.type === "assistant/message" && raw !== void 0) {
33421
33498
  throw new Error("assistant/message embeds its source stream and cannot carry sourceEventSeqs");
@@ -33453,7 +33530,7 @@ function validateSurfaceMetadata(event) {
33453
33530
  if (op !== void 0 && op !== "append" && (op.startSeq >= event.seq || op.endSeq >= event.seq)) {
33454
33531
  throw new Error(`surface replace at seq ${event.seq}: startSeq and endSeq must reference earlier events`);
33455
33532
  }
33456
- if (op !== void 0) assertProvenance(event, []);
33533
+ if (op !== void 0) assertSourceEventReferences(event, []);
33457
33534
  return op;
33458
33535
  }
33459
33536
  function replacementRange(state, op) {
@@ -33521,17 +33598,29 @@ function assertSystemHeadRewrite(event, state, startIdx, shadowedSeqs, events, b
33521
33598
  throw new Error("surface replace: node 0 holds the system prompt and may be rewritten only by a system/message over exactly that node");
33522
33599
  }
33523
33600
  }
33524
- function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
33601
+ function planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
33525
33602
  if (event.seq !== expectedSeq) {
33526
33603
  throw new Error(`session event seq ${event.seq} is not contiguous; expected ${expectedSeq}`);
33527
33604
  }
33528
33605
  const surfaceOp = validateSurfaceMetadata(event);
33606
+ const projection = projections.find((item) => item.type === event.type);
33607
+ if (projection !== void 0) {
33608
+ return { kind: "project", projection, messages: projection.project(event, {
33609
+ nodes: state.nodes,
33610
+ events,
33611
+ baseSeq,
33612
+ messages: state.projectedMessages
33613
+ }) };
33614
+ }
33615
+ if (MESSAGE_PROJECTION_EVENT_TYPES.has(event.type)) {
33616
+ throw new Error(`session event "${event.type}" requires a message projection; load its owning plugin or supply its projection definition`);
33617
+ }
33529
33618
  if (surfaceOp === void 0) return;
33530
33619
  if (surfaceOp === "append") {
33531
33620
  return { kind: "append", seq: event.seq };
33532
33621
  }
33533
33622
  const range = replacementRange(state, surfaceOp);
33534
- assertProvenance(event, range.shadowedSeqs);
33623
+ assertSourceEventReferences(event, range.shadowedSeqs);
33535
33624
  assertToolResultRewrite(event, range.shadowedSeqs, events, baseSeq);
33536
33625
  assertSystemHeadRewrite(event, state, range.startIdx, range.shadowedSeqs, events, baseSeq);
33537
33626
  return {
@@ -33542,8 +33631,8 @@ function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
33542
33631
  ...range
33543
33632
  };
33544
33633
  }
33545
- function applySurfaceEvent(state, event, expectedSeq, events, baseSeq) {
33546
- const plan = planSurfaceEvent(state, event, expectedSeq, events, baseSeq);
33634
+ function applySurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
33635
+ const plan = planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections);
33547
33636
  return applySurfacePlan(state, plan);
33548
33637
  }
33549
33638
  function applySurfacePlan(state, plan) {
@@ -33552,6 +33641,11 @@ function applySurfacePlan(state, plan) {
33552
33641
  } else if (plan?.kind === "replace") {
33553
33642
  state.nodes.splice(plan.startIdx, plan.endIdx - plan.startIdx + 1, plan.seq);
33554
33643
  state.replaceGeneration += 1;
33644
+ state.contentGeneration += 1;
33645
+ } else if (plan?.kind === "project") {
33646
+ for (const [seq, message] of plan.messages) state.projectedMessages.set(seq, message);
33647
+ state.projections.add(plan.projection);
33648
+ state.contentGeneration += 1;
33555
33649
  }
33556
33650
  if (plan?.kind !== "replace") return;
33557
33651
  return {
@@ -33565,14 +33659,17 @@ var SurfaceManager = class {
33565
33659
  /**
33566
33660
  * @param log - Contiguous complete log or loaded event window.
33567
33661
  * @param baseSeq - Absolute sequence of the window's first event.
33662
+ * @param projections - live borrowed definitions; removing a used definition invalidates further reads.
33568
33663
  */
33569
- constructor(log, baseSeq = SessionLogOffset(0)) {
33664
+ constructor(log, baseSeq = SessionLogOffset(0), projections = []) {
33570
33665
  this.log = log;
33571
33666
  this.baseSeq = baseSeq;
33667
+ this.projections = projections;
33572
33668
  this._lastProcessedSeq = baseSeq === 0 ? -1 : SessionSeq(baseSeq - 1);
33573
33669
  }
33574
33670
  log;
33575
33671
  baseSeq;
33672
+ projections;
33576
33673
  /** Shared transition state; replacement history is not retained. */
33577
33674
  _state = createFoldState();
33578
33675
  /** Last processed absolute seq. */
@@ -33584,21 +33681,40 @@ var SurfaceManager = class {
33584
33681
  * @param event - candidate event that has not entered the log yet.
33585
33682
  */
33586
33683
  validateNext(event) {
33684
+ this._assertProjections();
33587
33685
  if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
33588
33686
  const expectedSeq = SessionSeq(this.baseSeq + this.log.length);
33589
33687
  this._pendingPlan = {
33590
33688
  event,
33591
33689
  expectedSeq,
33592
- plan: planSurfaceEvent(this._state, event, expectedSeq, this.log, this.baseSeq)
33690
+ plan: planSurfaceEvent(this._state, event, expectedSeq, this.log, this.baseSeq, this.projections)
33593
33691
  };
33594
33692
  }
33595
33693
  /** Monotonic count of folded positional replacements. */
33596
33694
  get replaceGeneration() {
33695
+ this._assertProjections();
33597
33696
  if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
33598
33697
  return this._state.replaceGeneration;
33599
33698
  }
33699
+ /** Monotonic count of committed changes to existing model-visible content. */
33700
+ get contentGeneration() {
33701
+ this._assertProjections();
33702
+ if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
33703
+ return this._state.contentGeneration;
33704
+ }
33705
+ /**
33706
+ * Project one message with every committed message projection applied.
33707
+ * @param event - message-producing or log-only event.
33708
+ * @returns its immutable projected message, or null when it produces none.
33709
+ */
33710
+ deriveEventMessage(event) {
33711
+ this._assertProjections();
33712
+ if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
33713
+ return deriveEventMessage(event, this._state.projectedMessages);
33714
+ }
33600
33715
  /** Surface event sequences in model-visible order. */
33601
33716
  get nodes() {
33717
+ this._assertProjections();
33602
33718
  if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
33603
33719
  return this._state.nodes;
33604
33720
  }
@@ -33612,12 +33728,23 @@ var SurfaceManager = class {
33612
33728
  if (pending?.event === event && pending.expectedSeq === seq) {
33613
33729
  applySurfacePlan(this._state, pending.plan);
33614
33730
  } else {
33615
- applySurfaceEvent(this._state, event, SessionSeq(seq), this.log, this.baseSeq);
33731
+ applySurfaceEvent(this._state, event, SessionSeq(seq), this.log, this.baseSeq, this.projections);
33616
33732
  }
33617
33733
  if (pending !== void 0 && pending.expectedSeq <= seq) this._pendingPlan = void 0;
33618
33734
  this._lastProcessedSeq = SessionSeq(seq);
33619
33735
  }
33620
33736
  }
33737
+ /** Cached messages cannot outlive the definitions that interpreted their log. */
33738
+ _assertProjections() {
33739
+ const candidate = this._pendingPlan;
33740
+ const pending = candidate !== void 0 && this.log[candidate.expectedSeq - this.baseSeq] === candidate.event ? candidate.plan : void 0;
33741
+ const required2 = pending?.kind === "project" ? [...this._state.projections, pending.projection] : this._state.projections;
33742
+ for (const projection of required2) {
33743
+ if (!this.projections.includes(projection)) {
33744
+ throw new Error(`session message projection "${projection.type}" was removed or replaced; restore the session with its owning plugin`);
33745
+ }
33746
+ }
33747
+ }
33621
33748
  };
33622
33749
 
33623
33750
  // packages/core/session/src/request-header.ts
@@ -33860,7 +33987,7 @@ var attachments = /* @__PURE__ */ new WeakMap();
33860
33987
  var Session = class _Session {
33861
33988
  log = [];
33862
33989
  /** Single incremental owner of surface acceptance and projection state. */
33863
- surfaceManager = new SurfaceManager(this.log);
33990
+ surfaceManager;
33864
33991
  /** The ordered surface over this session's event log. */
33865
33992
  get surface() {
33866
33993
  return this.surfaceManager;
@@ -33911,10 +34038,12 @@ var Session = class _Session {
33911
34038
  * @param seed - optional borrowed replay or fork events.
33912
34039
  * @param header - optional borrowed storage metadata.
33913
34040
  * @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
34041
+ * @param projections - pure interpreters for plugin-owned message changes.
33914
34042
  * @returns a detached session.
34043
+ * @throws when a seed event requires a missing message interpreter or fails validation.
33915
34044
  */
33916
- static create(id, seed, header, inheritedEventCount) {
33917
- return new _Session(id, seed, header, "snapshot", inheritedEventCount);
34045
+ static create(id, seed, header, inheritedEventCount, projections) {
34046
+ return new _Session(id, seed, header, "snapshot", inheritedEventCount, projections);
33918
34047
  }
33919
34048
  /**
33920
34049
  * Restore a detached session by adopting an independently owned or deeply frozen seed.
@@ -33927,18 +34056,22 @@ var Session = class _Session {
33927
34056
  * @param header - independently owned storage metadata.
33928
34057
  * @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
33929
34058
  * @param eventState - aliasing state carried from the operation that produced the seed.
34059
+ * @param projections - pure interpreters for plugin-owned message changes.
33930
34060
  * @returns a restored detached session.
34061
+ * @throws when a seed event requires a missing message interpreter or fails validation.
33931
34062
  */
33932
- static fromRestore(id, seed, header, inheritedEventCount, eventState) {
34063
+ static fromRestore(id, seed, header, inheritedEventCount, eventState, projections) {
33933
34064
  return new _Session(
33934
34065
  id,
33935
34066
  seed,
33936
34067
  header,
33937
34068
  eventState,
33938
- inheritedEventCount
34069
+ inheritedEventCount,
34070
+ projections
33939
34071
  );
33940
34072
  }
33941
- constructor(id, seed, header, mode = "snapshot", suppliedInheritedEventCount) {
34073
+ constructor(id, seed, header, mode = "snapshot", suppliedInheritedEventCount, projections = []) {
34074
+ this.surfaceManager = new SurfaceManager(this.log, SessionLogOffset(0), projections);
33942
34075
  const restoredHeader = mode === "snapshot" ? void 0 : validateRestoredSessionHeader(id, header);
33943
34076
  if (seed !== void 0) {
33944
34077
  for (const [index2, source] of seed.entries()) {
@@ -34157,7 +34290,7 @@ var Session = class _Session {
34157
34290
  derived = [];
34158
34291
  /** Surface position (nodes projected) the cache has reached. */
34159
34292
  derivedNodes = 0;
34160
- /** {@link SurfaceManager.replaceGeneration} the cache was built under. */
34293
+ /** {@link SurfaceManager.contentGeneration} the cache was built under. */
34161
34294
  derivedGeneration = 0;
34162
34295
  /**
34163
34296
  * Derive the LLM message history by walking the ordered sequences of
@@ -34166,21 +34299,21 @@ var Session = class _Session {
34166
34299
  * append records its `surfaceOp`, so a raw event with no marker (a chunk, a
34167
34300
  * turn boundary) is correctly absent, and a compaction `replace` deletes the
34168
34301
  * shadowed nodes from the derivation. The projection rules are
34169
- * {@link deriveEventMessage}, folded per node.
34302
+ * {@link deriveEventMessage}, with logged message projections applied
34303
+ * without changing node membership or message identity.
34170
34304
  *
34171
- * CACHED: each surface node is projected exactly once, when first seen — a
34172
- * call costs O(new nodes), and a surface rewrite (a `replace`;
34173
- * {@link SessionSurface.replaceGeneration}) rebuilds. The returned array is
34305
+ * CACHED: pure tail growth costs O(new nodes); a replacement or message projection
34306
+ * ({@link SessionSurface.contentGeneration}) rebuilds. The returned array is
34174
34307
  * a fresh snapshot per call (later appends never grow an array a caller
34175
34308
  * already holds); the `Message` objects in it are SHARED and **deep-frozen**.
34176
- * Their content reuses the already frozen durable event data, so the cache
34177
- * needs no second deep clone and consumers still cannot mutate the log.
34309
+ * Unchanged content reuses frozen event data; projected blocks are frozen
34310
+ * derived copies. Consumers cannot mutate the log through either form.
34178
34311
  * @returns a fresh array of the shared, frozen derived history.
34179
34312
  */
34180
34313
  deriveMessages() {
34181
34314
  const surface = this.surface;
34182
34315
  const nodes = surface.nodes;
34183
- const generation = surface.replaceGeneration;
34316
+ const generation = surface.contentGeneration;
34184
34317
  if (generation !== this.derivedGeneration) {
34185
34318
  this.derived = [];
34186
34319
  this.derivedNodes = 0;
@@ -34194,13 +34327,13 @@ var Session = class _Session {
34194
34327
  return [...this.derived];
34195
34328
  }
34196
34329
  /**
34197
- * Instance face of the pure per-node `deriveEventMessage` export from
34198
- * `surface.ts`.
34330
+ * Project one event with all committed message projections applied.
34331
+ * The original durable event remains unchanged.
34199
34332
  * @param event - the event to project.
34200
34333
  * @returns the derived message, or null when the event produces none.
34201
34334
  */
34202
34335
  deriveEventMessage(event) {
34203
- return deriveEventMessage(event);
34336
+ return this.surfaceManager.deriveEventMessage(event);
34204
34337
  }
34205
34338
  };
34206
34339
 
@@ -55243,7 +55376,7 @@ var renderNodeToOutput = (node2, output, options) => {
55243
55376
  };
55244
55377
  var render_node_to_output_default = renderNodeToOutput;
55245
55378
 
55246
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/consts.js
55379
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/consts.js
55247
55380
  var BEL2 = "\x07";
55248
55381
  var ESC2 = "\x1B";
55249
55382
  var BACKSLASH = "\\";
@@ -55267,13 +55400,16 @@ var linkEndCode = `${ESC2}${OSC2}8;;${BEL2}`;
55267
55400
  var linkEndCodeST = `${ESC2}${OSC2}8;;${ESC2}${BACKSLASH}`;
55268
55401
  var linkEndCodeC1ST = `${ESC2}${OSC2}8;;${C1_ST}`;
55269
55402
 
55270
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/ansiCodes.js
55403
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/ansiCodes.js
55271
55404
  var endCodesSet = /* @__PURE__ */ new Set();
55272
55405
  var endCodesMap = /* @__PURE__ */ new Map();
55273
55406
  for (const [start, end] of ansi_styles_default.codes) {
55274
55407
  endCodesSet.add(ansi_styles_default.color.ansi(end));
55275
55408
  endCodesMap.set(ansi_styles_default.color.ansi(start), ansi_styles_default.color.ansi(end));
55276
55409
  }
55410
+ endCodesSet.add(linkEndCode);
55411
+ endCodesSet.add(linkEndCodeST);
55412
+ endCodesSet.add(linkEndCodeC1ST);
55277
55413
  function getEndCode(code2) {
55278
55414
  if (endCodesSet.has(code2))
55279
55415
  return code2;
@@ -55307,7 +55443,7 @@ function isIntensityCode(code2) {
55307
55443
  return code2.code === ansi_styles_default.bold.open || code2.code === ansi_styles_default.dim.open;
55308
55444
  }
55309
55445
 
55310
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/reduce.js
55446
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/reduce.js
55311
55447
  function reduceAnsiCodes(codes) {
55312
55448
  return reduceAnsiCodesIncremental([], codes);
55313
55449
  }
@@ -55332,7 +55468,7 @@ function reduceAnsiCodesIncremental(codes, newCodes) {
55332
55468
  return ret;
55333
55469
  }
55334
55470
 
55335
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/undo.js
55471
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/undo.js
55336
55472
  function undoAnsiCodes2(codes) {
55337
55473
  return reduceAnsiCodes(codes).reverse().map((code2) => ({
55338
55474
  ...code2,
@@ -55340,7 +55476,7 @@ function undoAnsiCodes2(codes) {
55340
55476
  }));
55341
55477
  }
55342
55478
 
55343
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/diff.js
55479
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/diff.js
55344
55480
  function diffAnsiCodes(from2, to) {
55345
55481
  const endCodesInTo = new Set(to.map((code2) => code2.endCode));
55346
55482
  const startCodesInTo = new Set(to.map((code2) => code2.code));
@@ -55359,7 +55495,7 @@ function diffAnsiCodes(from2, to) {
55359
55495
  ];
55360
55496
  }
55361
55497
 
55362
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/styledChars.js
55498
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/styledChars.js
55363
55499
  function styledCharsFromTokens(tokens) {
55364
55500
  let codes = [];
55365
55501
  const ret = [];
@@ -55392,7 +55528,7 @@ function styledCharsToString(chars) {
55392
55528
  return ret;
55393
55529
  }
55394
55530
 
55395
- // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.0/node_modules/@alcalzone/ansi-tokenize/build/tokenize.js
55531
+ // node_modules/.pnpm/@alcalzone+ansi-tokenize@0.3.1/node_modules/@alcalzone/ansi-tokenize/build/tokenize.js
55396
55532
  var segmenter3 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
55397
55533
  function isFullwidthGrapheme(grapheme, baseCodePoint) {
55398
55534
  if (isFullwidthCodePoint(baseCodePoint))
@@ -59298,7 +59434,28 @@ function titleRun(title) {
59298
59434
  }
59299
59435
  return [styled(title, "fg")];
59300
59436
  }
59301
- function AppShell({ title, badge, children, status, input }) {
59437
+ function topDividerLine(columns, label) {
59438
+ if (columns <= 0) return "";
59439
+ if (!label || label.trim() === "") {
59440
+ return paintRow([styled(escapeContent("\u2500".repeat(columns)), "line")]);
59441
+ }
59442
+ const escaped = escapeContent(label);
59443
+ const labelLen = displayWidth(escaped);
59444
+ const prefix = "\u2500\u2500\u2500 ";
59445
+ const prefixLen = displayWidth(prefix);
59446
+ const suffix = " \u2500\u2500\u2500";
59447
+ const suffixLen = displayWidth(suffix);
59448
+ if (prefixLen + labelLen + suffixLen >= columns) {
59449
+ return paintRow([styled(escapeContent("\u2500".repeat(columns)), "line")]);
59450
+ }
59451
+ const dashCount = Math.max(0, columns - prefixLen - labelLen - suffixLen);
59452
+ return paintRow([
59453
+ styled(prefix, "line"),
59454
+ styled(escaped, "accentText"),
59455
+ styled(`${suffix}${"\u2500".repeat(dashCount)}`, "line")
59456
+ ]);
59457
+ }
59458
+ function AppShell({ title, badge, children, status, input, topDividerLabel }) {
59302
59459
  const { columns, rows } = use_window_size_default();
59303
59460
  const fitted = layoutTitleBar(
59304
59461
  escapeContent(title),
@@ -59310,7 +59467,7 @@ function AppShell({ title, badge, children, status, input }) {
59310
59467
  if (fitted.badge !== "") titleParts.push(styled(fitted.badge, "fgDim"));
59311
59468
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", width: "100%", height: rows, children: [
59312
59469
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "row", width: "100%", flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: paintRow(titleParts) }) }),
59313
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: "100%", flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: paintRow([styled(escapeContent("\u2500".repeat(columns)), "line")]) }) }),
59470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: "100%", flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { wrap: "truncate", children: topDividerLine(columns, topDividerLabel) }) }),
59314
59471
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", flexGrow: 1, width: "100%", overflow: "hidden", children }),
59315
59472
  input !== void 0 && input !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "row", width: "100%", flexShrink: 0, children: input }) : null,
59316
59473
  status !== void 0 && status !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: "100%", flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: paintRow([styled(escapeContent("\u2500".repeat(columns)), "line")]) }) }) : null,
@@ -59468,6 +59625,25 @@ function producedPathsForTurn(cards) {
59468
59625
  return paths;
59469
59626
  }
59470
59627
 
59628
+ // packages/tui/tui-render/src/workspace-changes-card.ts
59629
+ var WORKSPACE_CHANGES_CARD_PATH_LIMIT = 3;
59630
+ function workspaceChangeCountsLabel(file2) {
59631
+ if (file2.binary === true) return "binary";
59632
+ if (file2.oversized === true) return "oversized";
59633
+ return `+${String(file2.added)}/\u2212${String(file2.deleted)}`;
59634
+ }
59635
+ function formatWorkspaceChangesCard(card, pathLimit = WORKSPACE_CHANGES_CARD_PATH_LIMIT) {
59636
+ if (card.files.length === 0) return void 0;
59637
+ const shown = card.files.slice(0, Math.max(0, pathLimit));
59638
+ const lines = [
59639
+ `\u6539\u52A8 \xB7 ${String(card.total)} \u4E2A\u6587\u4EF6 +${String(card.added)}/\u2212${String(card.deleted)}`,
59640
+ ...shown.map((file2) => ` ${file2.display} ${workspaceChangeCountsLabel(file2)}`)
59641
+ ];
59642
+ const hidden = card.total - shown.length;
59643
+ if (hidden > 0) lines.push(` \u2026 +${String(hidden)} \u4E2A\u6587\u4EF6`);
59644
+ return lines;
59645
+ }
59646
+
59471
59647
  // packages/tui/tui-render/src/stream-view.tsx
59472
59648
  import { pathToFileURL as pathToFileURL2 } from "node:url";
59473
59649
  import { isAbsolute as isAbsolute3 } from "node:path";
@@ -70475,10 +70651,21 @@ function ReasoningBlock({
70475
70651
  maxCols
70476
70652
  }) {
70477
70653
  const { columns } = use_window_size_default();
70478
- if (collapsed || text4 === "") return null;
70654
+ if (text4 === "") return null;
70479
70655
  const width = maxCols ?? columns;
70480
70656
  const escaped = escapeContent(text4);
70481
70657
  const body = wrapDisplayLines(escaped, Math.max(1, width - 4));
70658
+ if (collapsed) {
70659
+ const lineCount = body.length;
70660
+ const icon = live ? getBrailleSpinnerFrame(durationMs) : "\u273B";
70661
+ const parts = [
70662
+ styled("\u25B8 ", "accentText"),
70663
+ styled(`${icon} \u601D\u8003\u8FC7\u7A0B`, "accentText"),
70664
+ styled(` (\u5171 ${String(lineCount)} \u884C \xB7 ${formatSeconds(durationMs)}s)`, "fgDim"),
70665
+ styled(" \xB7 Ctrl+O \u5C55\u5F00", "fgDim")
70666
+ ];
70667
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { flexDirection: "column", width: "100%", backgroundColor: inkColor("toolBg"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { wrap: "truncate", children: paintBackgroundRow(parts, "toolBg", width > 0 ? width : 0) }) });
70668
+ }
70482
70669
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", width: "100%", backgroundColor: inkColor("toolBg"), children: [
70483
70670
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { wrap: "truncate", children: thinkingHeader(durationMs, !live, live, width) }),
70484
70671
  body.map((line5, index2) => bodyRow(line5, index2, width))
@@ -71962,6 +72149,44 @@ function physicalScrollRailGeometry(contentRows, viewportRows, offsetFromBottom)
71962
72149
  return { rows, thumbStart: travel - fromBottom, thumbRows };
71963
72150
  }
71964
72151
 
72152
+ // packages/tui/tui-render/src/sticky-header.ts
72153
+ function collapseStickyText(text4) {
72154
+ return text4.replace(/\s+/gu, " ").trim();
72155
+ }
72156
+ function formatStickyThinkingLabel(range) {
72157
+ return `\u{1F4AD} \u601D\u8003\u4E2D (\u7B2C ${String(range.turnOrdinal)} \u8F6E \xB7 ${(range.durationMs / 1e3).toFixed(1)}s) \xB7 [Ctrl+O \u6298\u53E0]`;
72158
+ }
72159
+ function formatStickyToolLabel(range) {
72160
+ const title = collapseStickyText(range.title);
72161
+ return title === "" ? "\u25B8" : `\u25B8 ${title}`;
72162
+ }
72163
+ function formatStickyUserLabel(range) {
72164
+ const text4 = collapseStickyText(range.text);
72165
+ return text4 === "" ? ">" : `> ${text4}`;
72166
+ }
72167
+ function resolveStickyThinkingHeader(viewportTop, ranges) {
72168
+ for (const range of ranges) {
72169
+ if (viewportTop > range.start && viewportTop < range.end) {
72170
+ return formatStickyThinkingLabel(range);
72171
+ }
72172
+ }
72173
+ return void 0;
72174
+ }
72175
+ function resolveStickyTranscriptHeader(viewportTop, ranges) {
72176
+ const thinking = resolveStickyThinkingHeader(viewportTop, ranges.reasoning ?? []);
72177
+ if (thinking !== void 0) return thinking;
72178
+ for (const range of ranges.tools ?? []) {
72179
+ if (viewportTop > range.start && viewportTop < range.end) {
72180
+ return formatStickyToolLabel(range);
72181
+ }
72182
+ }
72183
+ const latestUser = ranges.users?.at(-1);
72184
+ if (latestUser !== void 0 && viewportTop >= latestUser.end) {
72185
+ return formatStickyUserLabel(latestUser);
72186
+ }
72187
+ return void 0;
72188
+ }
72189
+
71965
72190
  // packages/tui/tui-render/src/transcript-layout-cache.ts
71966
72191
  var TranscriptLayoutCache = class {
71967
72192
  scopeKey = "";
@@ -73581,7 +73806,7 @@ function projectBlockRows(entry, scope, markdownState) {
73581
73806
  case "divider":
73582
73807
  return projectDividerEntry(entry, scope);
73583
73808
  case "compaction":
73584
- return projectCompactionEntry(entry);
73809
+ return projectCompactionEntry(entry, scope);
73585
73810
  case "turn-tail":
73586
73811
  return projectTurnTailEntry(entry, scope);
73587
73812
  case "tool-summary":
@@ -73816,9 +74041,21 @@ function projectReasoningEntry(entry, scope) {
73816
74041
  const expanded = entry.meta?.reasoningExpanded === true;
73817
74042
  const live = entry.meta?.reasoningLive === true;
73818
74043
  const secondsLabel = (reasoningDurationMs / 1e3).toFixed(1);
73819
- if (!expanded || entry.source === "") {
74044
+ if (entry.source === "") {
73820
74045
  return { revision: 0, sourceLength: entry.source.length, lines: [] };
73821
74046
  }
74047
+ const escaped = escapeContent(entry.source);
74048
+ const body = wrapDisplayLines(escaped, Math.max(1, scope.width - 4));
74049
+ if (!expanded) {
74050
+ const icon2 = live ? getBrailleSpinnerFrame(reasoningDurationMs) : "\u273B";
74051
+ const capsule = surfaceLine([
74052
+ { text: "\u25B8 ", token: "accentText", bold: false },
74053
+ { text: `${icon2} \u601D\u8003\u8FC7\u7A0B`, token: "accentText", bold: false },
74054
+ { text: ` (\u5171 ${String(body.length)} \u884C \xB7 ${secondsLabel}s)`, token: "fgDim", bold: false },
74055
+ { text: " \xB7 Ctrl+O \u5C55\u5F00", token: "fgDim", bold: false }
74056
+ ], 0, "toolBg", scope.width);
74057
+ return { revision: 0, sourceLength: entry.source.length, lines: [capsule] };
74058
+ }
73822
74059
  const icon = live ? getBrailleSpinnerFrame(reasoningDurationMs) : "\u273B";
73823
74060
  const prefix = live ? "" : "\u25BE ";
73824
74061
  const headerLine = surfaceLine([
@@ -73827,8 +74064,6 @@ function projectReasoningEntry(entry, scope) {
73827
74064
  { text: ` (${secondsLabel}s)`, token: "fgDim", bold: false }
73828
74065
  ], 0, "toolBg", scope.width);
73829
74066
  const lines = [headerLine];
73830
- const escaped = escapeContent(entry.source);
73831
- const body = wrapDisplayLines(escaped, Math.max(1, scope.width - 4));
73832
74067
  for (const row of body) {
73833
74068
  const text4 = `\u2502 ${row}`;
73834
74069
  const cols = displayWidth(text4);
@@ -73866,25 +74101,36 @@ function projectDividerEntry(entry, scope) {
73866
74101
  const line5 = lineForText(entry.source, "fgDim", false, 0);
73867
74102
  return { revision: 0, sourceLength: entry.source.length, lines: [line5] };
73868
74103
  }
73869
- function projectCompactionEntry(entry) {
74104
+ function projectCompactionEntry(entry, scope) {
73870
74105
  const shadowed = entry.meta?.compactionShadowedCount;
73871
74106
  const countLabel = shadowed === void 0 ? "" : ` ${String(shadowed)} \u6761`;
74107
+ const tokens = entry.meta?.compactionShadowedTokenCount;
74108
+ const tokenLabel = tokens === void 0 ? "" : ` \xB7 ~${String(tokens)} tok`;
73872
74109
  const expanded = entry.meta?.compactionExpanded === true;
73873
- const marker = `\u2500\u2500\u2500\u2500 \u2702 \u5DF2\u538B\u7F29${countLabel} \xB7 Ctrl+K ${expanded ? "\u6298\u53E0" : "\u5C55\u5F00"} \u2500\u2500\u2500\u2500`;
74110
+ const marker = `\u2500\u2500\u2500\u2500 \u2702 \u5DF2\u538B\u7F29${countLabel}${tokenLabel} \xB7 Ctrl+K ${expanded ? "\u6298\u53E0" : "\u5C55\u5F00"} \u2500\u2500\u2500\u2500`;
73874
74111
  const lines = [
73875
74112
  lineForText(marker, "fgDim", false, 0)
73876
74113
  ];
73877
74114
  const summary = entry.meta?.compactionSummary ?? "";
73878
74115
  if (expanded && summary !== "") {
73879
- lines.push(mixedLine([
73880
- { text: "\u6458\u8981 ", token: "fgDim", bold: false },
73881
- { text: escapeContent(summary), token: "fg", bold: false }
73882
- ], lines.length));
74116
+ const wrapped = wrapDisplayLines(escapeContent(summary), Math.max(1, scope.width - 4));
74117
+ for (const row of wrapped) {
74118
+ lines.push(surfaceLine([
74119
+ { text: "\u2502 ", token: "accentText", bold: false },
74120
+ { text: row, token: "fg", bold: false }
74121
+ ], lines.length, "toolBg", scope.width));
74122
+ }
73883
74123
  }
73884
74124
  return { revision: 0, sourceLength: entry.source.length, lines };
73885
74125
  }
73886
74126
  function projectTurnTailEntry(entry, scope) {
73887
74127
  const lines = [];
74128
+ const changeLines = entry.meta?.turnTailWorkspaceChanges === void 0 ? void 0 : formatWorkspaceChangesCard(entry.meta.turnTailWorkspaceChanges);
74129
+ if (changeLines !== void 0) {
74130
+ for (const text4 of changeLines) {
74131
+ lines.push(lineForText(escapeContent(text4), "fg", false, lines.length));
74132
+ }
74133
+ }
73888
74134
  const produced = entry.meta?.turnTailProduced ?? [];
73889
74135
  if (produced.length > 0) {
73890
74136
  const joined = produced.map(escapeContent).join(" \xB7 ");
@@ -74759,8 +75005,8 @@ function isVisiblePart(part) {
74759
75005
  if (part.kind === "card" || part.kind === "tool-summary") return true;
74760
75006
  return part.text !== "";
74761
75007
  }
74762
- function displayedParts(parts, reasoningExpanded) {
74763
- return parts.filter((part) => isVisiblePart(part) && (part.kind !== "reasoning" || reasoningExpanded));
75008
+ function displayedParts(parts, _reasoningExpanded) {
75009
+ return parts.filter(isVisiblePart);
74764
75010
  }
74765
75011
  var COLLAPSED_TOOL_CARD_LIMIT = 3;
74766
75012
  function toolSummaryStatus(summary) {
@@ -74852,7 +75098,8 @@ function activeTurnVersion(turn, revisions) {
74852
75098
  }
74853
75099
  function compactionDividerLabel(divider, expanded) {
74854
75100
  const count = divider.shadowedCount === void 0 ? "" : ` ${String(divider.shadowedCount)} \u6761`;
74855
- return `\u2500\u2500\u2500\u2500 \u2702 \u5DF2\u538B\u7F29${count} \xB7 Ctrl+K ${expanded ? "\u6298\u53E0" : "\u5C55\u5F00"} \u2500\u2500\u2500\u2500`;
75101
+ const tokens = divider.shadowedTokenCount === void 0 ? "" : ` \xB7 ~${String(divider.shadowedTokenCount)} tok`;
75102
+ return `\u2500\u2500\u2500\u2500 \u2702 \u5DF2\u538B\u7F29${count}${tokens} \xB7 Ctrl+K ${expanded ? "\u6298\u53E0" : "\u5C55\u5F00"} \u2500\u2500\u2500\u2500`;
74856
75103
  }
74857
75104
  function partsFrom(content3) {
74858
75105
  const parts = [];
@@ -75191,6 +75438,16 @@ function projectorStateFor(cache4, blockId, scope, aliases = [], source) {
75191
75438
  cache4.set(key, state);
75192
75439
  return state;
75193
75440
  }
75441
+ function stickyToolTitle(card) {
75442
+ return card.resultView?.title ?? card.callView?.title ?? card.name;
75443
+ }
75444
+ function recordToolSticky(map2, id, start, end, card) {
75445
+ if (end - start <= 1) return;
75446
+ const existing = map2.get(id);
75447
+ const next = existing === void 0 ? [] : existing;
75448
+ next.push({ start, end, title: stickyToolTitle(card) });
75449
+ map2.set(id, next);
75450
+ }
75194
75451
  var EMPTY_TEXT_RANGES = [];
75195
75452
  var GAP_LINE = Object.freeze({
75196
75453
  text: " ",
@@ -75370,7 +75627,8 @@ function StreamView({
75370
75627
  motionPaused = false,
75371
75628
  mode = "agent",
75372
75629
  layoutKey,
75373
- locale = "zh-CN"
75630
+ locale = "zh-CN",
75631
+ onStickyHeaderChange
75374
75632
  }) {
75375
75633
  const policy = renderPolicy ?? renderPolicyDefaults();
75376
75634
  const toolRows = (0, import_react37.useMemo)(() => new ToolRowCache(policy.tools), [
@@ -75591,6 +75849,9 @@ function StreamView({
75591
75849
  const map2 = /* @__PURE__ */ new Map();
75592
75850
  const textRanges = /* @__PURE__ */ new Map();
75593
75851
  const storeBlocks = /* @__PURE__ */ new Map();
75852
+ const reasoningSticky = /* @__PURE__ */ new Map();
75853
+ const toolSticky = /* @__PURE__ */ new Map();
75854
+ const userSticky = /* @__PURE__ */ new Map();
75594
75855
  const projectorCache = projectorStates.current;
75595
75856
  const project = makeBlockProjector({ toolRows, plainRows, locale }, storeBlocks);
75596
75857
  const latestAssistant = hasActiveTurn ? void 0 : latestAssistantId(history, void 0);
@@ -75600,7 +75861,8 @@ function StreamView({
75600
75861
  const meta3 = {
75601
75862
  compactionSummary: row.divider.summary,
75602
75863
  compactionExpanded: expandedCompactionId === row.divider.compactionId,
75603
- ...row.divider.shadowedCount === void 0 ? {} : { compactionShadowedCount: row.divider.shadowedCount }
75864
+ ...row.divider.shadowedCount === void 0 ? {} : { compactionShadowedCount: row.divider.shadowedCount },
75865
+ ...row.divider.shadowedTokenCount === void 0 ? {} : { compactionShadowedTokenCount: row.divider.shadowedTokenCount }
75604
75866
  };
75605
75867
  const projection = project(id, {
75606
75868
  id,
@@ -75620,6 +75882,7 @@ function StreamView({
75620
75882
  ...hasSubsequent ? { meta: { userMessageGap: true } } : {}
75621
75883
  }, settledBlockRowsScope, void 0, false);
75622
75884
  map2.set(id, projection.lines);
75885
+ userSticky.set(id, { start: 0, end: projection.lines.length, text: row.message.text });
75623
75886
  continue;
75624
75887
  }
75625
75888
  const parts = displayedParts(
@@ -75632,6 +75895,7 @@ function StreamView({
75632
75895
  for (const [partIndex, part] of parts.entries()) {
75633
75896
  if (turnPartGap(parts, partIndex) > 0) rows2.push(GAP_LINE);
75634
75897
  if (part.kind === "reasoning") {
75898
+ const stickyStart = rows2.length;
75635
75899
  rows2.append(project(id, {
75636
75900
  id: `${id}-r-${String(partIndex)}`,
75637
75901
  kind: "reasoning",
@@ -75642,6 +75906,17 @@ function StreamView({
75642
75906
  reasoningLive: false
75643
75907
  }
75644
75908
  }, settledBlockRowsScope, void 0, false).lines);
75909
+ if (reasoningExpanded && rows2.length - stickyStart > 1) {
75910
+ const existing = reasoningSticky.get(id);
75911
+ const next = existing === void 0 ? [] : [...existing];
75912
+ next.push({
75913
+ start: stickyStart,
75914
+ end: rows2.length,
75915
+ turnOrdinal: row.message.turnOrdinal ?? 1,
75916
+ durationMs: part.durationMs
75917
+ });
75918
+ reasoningSticky.set(id, next);
75919
+ }
75645
75920
  continue;
75646
75921
  }
75647
75922
  if (part.kind === "tool-summary") {
@@ -75655,6 +75930,7 @@ function StreamView({
75655
75930
  continue;
75656
75931
  }
75657
75932
  if (part.kind === "card") {
75933
+ const stickyStart = rows2.length;
75658
75934
  rows2.append(project(
75659
75935
  id,
75660
75936
  makeToolCardBlockEntry(id, part.card),
@@ -75662,6 +75938,7 @@ function StreamView({
75662
75938
  void 0,
75663
75939
  false
75664
75940
  ).lines);
75941
+ recordToolSticky(toolSticky, id, stickyStart, rows2.length, part.card);
75665
75942
  continue;
75666
75943
  }
75667
75944
  const start = rows2.length;
@@ -75696,7 +75973,8 @@ function StreamView({
75696
75973
  );
75697
75974
  const produced = producedPathsForTurn(cards);
75698
75975
  return produced.length === 0 ? {} : { turnTailProduced: produced };
75699
- })()
75976
+ })(),
75977
+ ...row.message.workspaceChanges === void 0 ? {} : { turnTailWorkspaceChanges: row.message.workspaceChanges }
75700
75978
  };
75701
75979
  if (Object.keys(tailMeta).length > 0) {
75702
75980
  rows2.push(GAP_LINE);
@@ -75710,7 +75988,7 @@ function StreamView({
75710
75988
  map2.set(id, rows2.build());
75711
75989
  if (ranges.length > 0) textRanges.set(id, ranges);
75712
75990
  }
75713
- return { rows: map2, textRanges, storeBlocks };
75991
+ return { rows: map2, textRanges, storeBlocks, reasoningSticky, toolSticky, userSticky };
75714
75992
  }, [
75715
75993
  contentWidth,
75716
75994
  expandedCompactionId,
@@ -75755,7 +76033,7 @@ function StreamView({
75755
76033
  )}s)`
75756
76034
  }
75757
76035
  }, blockRowsScope, void 0, true).lines;
75758
- return { id, lines, ranges: [], storeBlocks };
76036
+ return { id, lines, ranges: [], storeBlocks, reasoningSticky: [], toolSticky: [] };
75759
76037
  }
75760
76038
  const parts = displayedParts(
75761
76039
  compactToolParts(rawParts, toolCardsExpanded, batchPresenters, mode, presenterCache),
@@ -75763,6 +76041,8 @@ function StreamView({
75763
76041
  );
75764
76042
  const rows2 = new RowSequence();
75765
76043
  const ranges = [];
76044
+ const sticky = [];
76045
+ const toolSticky = [];
75766
76046
  let textIndex = 0;
75767
76047
  let lastVisiblePart = -1;
75768
76048
  for (const [partIndex, part] of parts.entries()) {
@@ -75772,16 +76052,26 @@ function StreamView({
75772
76052
  if (turnPartGap(parts, partIndex) > 0) rows2.push(GAP_LINE);
75773
76053
  if (part.kind === "reasoning") {
75774
76054
  const live = status === "generating" && partIndex === lastVisiblePart;
76055
+ const durationMs = live ? liveDurationMs ?? part.durationMs : part.durationMs;
76056
+ const stickyStart = rows2.length;
75775
76057
  rows2.append(project(id, {
75776
76058
  id: `${id}-r-${String(partIndex)}`,
75777
76059
  kind: "reasoning",
75778
76060
  source: part.text,
75779
76061
  meta: {
75780
- reasoningDurationMs: live ? liveDurationMs ?? part.durationMs : part.durationMs,
76062
+ reasoningDurationMs: durationMs,
75781
76063
  reasoningExpanded,
75782
76064
  reasoningLive: live
75783
76065
  }
75784
76066
  }, blockRowsScope, void 0, status === "generating").lines);
76067
+ if (reasoningExpanded && rows2.length - stickyStart > 1) {
76068
+ sticky.push({
76069
+ start: stickyStart,
76070
+ end: rows2.length,
76071
+ turnOrdinal: activeTurn.turn,
76072
+ durationMs
76073
+ });
76074
+ }
75785
76075
  continue;
75786
76076
  }
75787
76077
  if (part.kind === "tool-summary") {
@@ -75795,6 +76085,7 @@ function StreamView({
75795
76085
  continue;
75796
76086
  }
75797
76087
  if (part.kind === "card") {
76088
+ const stickyStart = rows2.length;
75798
76089
  rows2.append(project(
75799
76090
  id,
75800
76091
  makeToolCardBlockEntry(id, part.card),
@@ -75802,6 +76093,13 @@ function StreamView({
75802
76093
  void 0,
75803
76094
  status === "generating"
75804
76095
  ).lines);
76096
+ if (rows2.length - stickyStart > 1) {
76097
+ toolSticky.push({
76098
+ start: stickyStart,
76099
+ end: rows2.length,
76100
+ title: stickyToolTitle(part.card)
76101
+ });
76102
+ }
75805
76103
  continue;
75806
76104
  }
75807
76105
  const start = rows2.length;
@@ -75822,7 +76120,7 @@ function StreamView({
75822
76120
  const lastVisible = parts[parts.length - 1];
75823
76121
  const isReasoningActive = lastRaw?.kind === "reasoning";
75824
76122
  const isToolCompleted = lastVisible?.kind === "card" && lastVisible.card.status !== "running" || lastVisible?.kind === "tool-summary" && lastVisible.summary.runningCount === 0;
75825
- if (isReasoningActive && !reasoningExpanded || isToolCompleted) {
76123
+ if (isToolCompleted && !(isReasoningActive && !reasoningExpanded)) {
75826
76124
  const liveMs = liveDurationMs ?? activeTurn.reasoningDurationMs;
75827
76125
  const spinner = getBrailleSpinnerFrame(liveMs);
75828
76126
  const label = isReasoningActive ? tuiCopy("thinking", locale) : tuiCopy("processing", locale);
@@ -75837,7 +76135,7 @@ function StreamView({
75837
76135
  }, blockRowsScope, void 0, true).lines);
75838
76136
  }
75839
76137
  }
75840
- return { id, lines: rows2.build(), ranges, storeBlocks };
76138
+ return { id, lines: rows2.build(), ranges, storeBlocks, reasoningSticky: sticky, toolSticky };
75841
76139
  }, [
75842
76140
  activeTurn,
75843
76141
  activeVersion,
@@ -75867,7 +76165,15 @@ function StreamView({
75867
76165
  for (const [key, val] of activeTurnRows.storeBlocks) {
75868
76166
  storeBlocks.set(key, val);
75869
76167
  }
75870
- return { rows: map2, textRanges, storeBlocks };
76168
+ const reasoningSticky = new Map(settledEntryRows.reasoningSticky);
76169
+ if (activeTurnRows.reasoningSticky.length > 0) {
76170
+ reasoningSticky.set(activeTurnRows.id, activeTurnRows.reasoningSticky);
76171
+ }
76172
+ const toolSticky = new Map(settledEntryRows.toolSticky);
76173
+ if (activeTurnRows.toolSticky.length > 0) {
76174
+ toolSticky.set(activeTurnRows.id, activeTurnRows.toolSticky);
76175
+ }
76176
+ return { rows: map2, textRanges, storeBlocks, reasoningSticky, toolSticky, userSticky: settledEntryRows.userSticky };
75871
76177
  }, [settledEntryRows, activeTurnRows]);
75872
76178
  const [presentedEntryRows, setPresentedEntryRows] = (0, import_react37.useState)(entryRows);
75873
76179
  const activeEntryRows = status === "generating" ? presentedEntryRows : entryRows;
@@ -76103,6 +76409,58 @@ function StreamView({
76103
76409
  virtualContentRows - effectiveViewportRows - effectiveOffset
76104
76410
  );
76105
76411
  const visibleBottom = visibleTop + effectiveViewportRows;
76412
+ const stickyRanges = (0, import_react37.useMemo)(() => {
76413
+ const reasoning = [];
76414
+ const tools = [];
76415
+ const users = [];
76416
+ for (const layout of virtualLayouts) {
76417
+ if (reasoningExpanded) {
76418
+ const relative = activeEntryRows.reasoningSticky.get(layout.id);
76419
+ if (relative !== void 0) {
76420
+ for (const range of relative) {
76421
+ reasoning.push({
76422
+ start: layout.top + range.start,
76423
+ end: layout.top + range.end,
76424
+ turnOrdinal: range.turnOrdinal,
76425
+ durationMs: range.durationMs
76426
+ });
76427
+ }
76428
+ }
76429
+ }
76430
+ const toolRelative = activeEntryRows.toolSticky.get(layout.id);
76431
+ if (toolRelative !== void 0) {
76432
+ for (const range of toolRelative) {
76433
+ tools.push({
76434
+ start: layout.top + range.start,
76435
+ end: layout.top + range.end,
76436
+ title: range.title
76437
+ });
76438
+ }
76439
+ }
76440
+ const userRelative = activeEntryRows.userSticky.get(layout.id);
76441
+ if (userRelative !== void 0) {
76442
+ users.push({
76443
+ start: layout.top + userRelative.start,
76444
+ end: layout.top + userRelative.end,
76445
+ text: userRelative.text
76446
+ });
76447
+ }
76448
+ }
76449
+ return { reasoning, tools, users };
76450
+ }, [
76451
+ activeEntryRows.reasoningSticky,
76452
+ activeEntryRows.toolSticky,
76453
+ activeEntryRows.userSticky,
76454
+ reasoningExpanded,
76455
+ virtualLayouts
76456
+ ]);
76457
+ const stickyLabel = resolveStickyTranscriptHeader(visibleTop, stickyRanges);
76458
+ (0, import_react37.useLayoutEffect)(() => {
76459
+ onStickyHeaderChange?.(stickyLabel);
76460
+ return () => {
76461
+ onStickyHeaderChange?.(void 0);
76462
+ };
76463
+ }, [onStickyHeaderChange, stickyLabel]);
76106
76464
  const configuredOverscanRows = Math.max(
76107
76465
  0,
76108
76466
  Math.min(
@@ -76338,7 +76696,6 @@ function StreamView({
76338
76696
  } : void 0;
76339
76697
  const visibleFrame = (0, import_react37.useMemo)(() => {
76340
76698
  const snapshotRows = [];
76341
- const verticalBase = Math.max(0, effectiveViewportRows - virtualContentRows);
76342
76699
  for (const { entry, index: layoutIndex } of visibleEntryPairs) {
76343
76700
  const layout = virtualLayouts[layoutIndex];
76344
76701
  if (layout === void 0) continue;
@@ -76349,7 +76706,7 @@ function StreamView({
76349
76706
  for (let index2 = start; index2 < end; index2 += 1) {
76350
76707
  const line5 = entry.lines.at(index2);
76351
76708
  if (line5 === void 0) continue;
76352
- const absoluteRow = 3 + verticalBase + layout.top + index2 - visibleTop;
76709
+ const absoluteRow = 3 + layout.top + index2 - visibleTop;
76353
76710
  if (absoluteRow < 3 || absoluteRow >= 3 + effectiveViewportRows) continue;
76354
76711
  const isBottomRow = absoluteRow === 3 + effectiveViewportRows - 1;
76355
76712
  const promptOverlay = isBottomRow && !viewport.follow ? (() => {
@@ -76486,13 +76843,14 @@ function StreamView({
76486
76843
  (card) => presenterCache.present(presenters, card)
76487
76844
  );
76488
76845
  const produced = producedPathsForTurn(cards);
76846
+ const changeLines = message.workspaceChanges === void 0 ? void 0 : formatWorkspaceChangesCard(message.workspaceChanges);
76489
76847
  const stats = formatTurnTailStats({
76490
76848
  turnOrdinal: message.turnOrdinal,
76491
76849
  turnUsage: message.turnUsage,
76492
76850
  legacyOutputTokens: message.usageOutputTokens,
76493
76851
  elapsedMs: message.stepWallMs
76494
76852
  });
76495
- if (produced.length === 0 && stats === void 0 && !showCompletionBoundary) return void 0;
76853
+ if (produced.length === 0 && changeLines === void 0 && stats === void 0 && !showCompletionBoundary) return void 0;
76496
76854
  const producedRows = [];
76497
76855
  let rowRuns = [styled("\u4EA7\u7269 \xB7 ", "fg")];
76498
76856
  let rowWidth = displayWidth("\u4EA7\u7269 \xB7 ");
@@ -76516,6 +76874,7 @@ function StreamView({
76516
76874
  }
76517
76875
  if (pathsInRow > 0) producedRows.push(rowRuns);
76518
76876
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", width: "100%", children: [
76877
+ (changeLines ?? []).map((line5, index2) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: paintRow([styled(escapeContent(line5), "fg")]) }, `changes-${String(index2)}`)),
76519
76878
  producedRows.map((runs, index2) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: paintRow(runs) }, `produced-${String(index2)}`)),
76520
76879
  stats === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: paintRow([styled(escapeContent(stats), "fgDim")]) }),
76521
76880
  showCompletionBoundary ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: paintRow([styled("\u2500\u2500 \u5DF2\u5B8C\u6210 \u2500\u2500", "fgDim")]) }) : null
@@ -76560,10 +76919,10 @@ function StreamView({
76560
76919
  const expanded = divider.compactionId === expandedCompactionId;
76561
76920
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", width: "100%", children: [
76562
76921
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: paintRow([styled(escapeContent(compactionDividerLabel(divider, expanded)), "fgDim")]) }),
76563
- expanded && divider.summary !== "" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: paintRow([
76564
- styled("\u6458\u8981 ", "fgDim"),
76565
- styled(escapeContent(divider.summary), "fg")
76566
- ]) }) : null
76922
+ expanded && divider.summary !== "" ? wrapDisplayLines(escapeContent(divider.summary), Math.max(1, contentWidth - 4)).map((row, index2) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { wrap: "truncate", children: paintBackgroundRow([
76923
+ styled("\u2502 ", "accentText"),
76924
+ styled(row, "fg")
76925
+ ], "toolBg", contentWidth) }, index2)) : null
76567
76926
  ] });
76568
76927
  };
76569
76928
  const renderActiveTurn = (turn) => {
@@ -76597,7 +76956,7 @@ function StreamView({
76597
76956
  const lastVisible = parts[parts.length - 1];
76598
76957
  const isReasoningActive = lastRaw?.kind === "reasoning";
76599
76958
  const isToolCompleted = lastVisible?.kind === "card" && lastVisible.card.status !== "running" || lastVisible?.kind === "tool-summary" && lastVisible.summary.runningCount === 0;
76600
- const showTail = generating && (isReasoningActive && !reasoningExpanded || isToolCompleted);
76959
+ const showTail = generating && isToolCompleted && !(isReasoningActive && !reasoningExpanded);
76601
76960
  const tailLiveMs = liveDurationMs ?? turn.reasoningDurationMs;
76602
76961
  const tailSpinner = getBrailleSpinnerFrame(tailLiveMs);
76603
76962
  const tailLabel = isReasoningActive ? tuiCopy("thinking", locale) : tuiCopy("processing", locale);
@@ -76656,7 +77015,8 @@ function StreamView({
76656
77015
  {
76657
77016
  flexDirection: "column",
76658
77017
  position: physicalViewport ? "absolute" : "relative",
76659
- bottom: physicalViewport ? -effectiveOffset : void 0,
77018
+ top: physicalViewport && virtualContentRows <= effectiveViewportRows ? 0 : void 0,
77019
+ bottom: physicalViewport && virtualContentRows > effectiveViewportRows ? -effectiveOffset : void 0,
76660
77020
  left: physicalViewport ? contentLeft : void 0,
76661
77021
  marginLeft: physicalViewport ? void 0 : contentLeft,
76662
77022
  width: contentWidth,
@@ -77322,14 +77682,29 @@ function hubRow(row, selected) {
77322
77682
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent(`\u5B50\u4F1A\u8BDD ID \xB7 ${row.id}`), "fgDim")]) })
77323
77683
  ] }, row.id);
77324
77684
  }
77685
+ function hubLimitsFootnote(liveContinuable, maxActiveSubagents, maxDepth) {
77686
+ if (maxActiveSubagents === void 0 && maxDepth === void 0) {
77687
+ return void 0;
77688
+ }
77689
+ const live = liveContinuable ?? 0;
77690
+ const max = maxActiveSubagents ?? 8;
77691
+ const depth = maxDepth ?? 1;
77692
+ const base = `\u53EF\u7EE7\u7EED ${String(live)}/${String(max)} live \xB7 \u6DF1\u5EA6 ${String(depth)}`;
77693
+ return live >= max ? `${base} \xB7 \u5DF2\u8FBE\u4E0A\u9650` : base;
77694
+ }
77325
77695
  function AgentHubPane({
77326
77696
  rows = [],
77327
77697
  selectedIndex = 0,
77328
77698
  view = "table",
77329
77699
  error: error51,
77330
77700
  transcript,
77331
- missing = false
77701
+ missing = false,
77702
+ liveContinuable,
77703
+ maxActiveSubagents,
77704
+ maxDepth,
77705
+ scrollOffset
77332
77706
  }) {
77707
+ const limits = hubLimitsFootnote(liveContinuable, maxActiveSubagents, maxDepth);
77333
77708
  if (missing) {
77334
77709
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
77335
77710
  OverlayShell,
@@ -77340,6 +77715,7 @@ function AgentHubPane({
77340
77715
  }
77341
77716
  );
77342
77717
  }
77718
+ const clamped = Math.min(Math.max(selectedIndex, 0), Math.max(0, rows.length - 1));
77343
77719
  if (view === "transcript") {
77344
77720
  if (error51 !== void 0 && error51 !== "") {
77345
77721
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
@@ -77352,7 +77728,39 @@ function AgentHubPane({
77352
77728
  );
77353
77729
  }
77354
77730
  const lines = (transcript ?? "").split("\n").filter((line5) => line5 !== "");
77355
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OverlayShell, { title: "\u5B50\u4EE3\u7406", footnote: "Esc \u8FD4\u56DE", children: lines.map((line5, index2) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent(line5), "fg")]) }, index2)) });
77731
+ const selectedRow = rows[clamped];
77732
+ const headerSegments = selectedRow === void 0 ? [] : [
77733
+ `[\u5B50\u4EE3\u7406] ${selectedRow.label}`,
77734
+ selectedRow.activity === "" ? void 0 : `\u72B6\u6001: ${selectedRow.activity}`,
77735
+ ...metricSegments(selectedRow)
77736
+ ].filter((s) => s !== void 0);
77737
+ const total = lines.length;
77738
+ const pageSize = 20;
77739
+ const start = Math.min(scrollOffset ?? 0, Math.max(0, total - 1));
77740
+ const slice = lines.slice(start, start + pageSize);
77741
+ const scrollInfo = total > pageSize ? ` (${String(start + 1)}-${String(Math.min(start + slice.length, total))}/${String(total)})` : "";
77742
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
77743
+ OverlayShell,
77744
+ {
77745
+ title: "\u5B50\u4EE3\u7406",
77746
+ footnote: `\u2191\u2193/jk \u6EDA\u52A8${scrollInfo} \xB7 Esc \u8FD4\u56DE`,
77747
+ children: [
77748
+ headerSegments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { width: "100%", flexDirection: "column", marginY: 0, children: [
77749
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent(headerSegments.join(" \xB7 ")), "accent", void 0, true)]) }),
77750
+ selectedRow?.id && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent(`\u5B50\u4F1A\u8BDD ID \xB7 ${selectedRow.id}`), "fgDim")]) }),
77751
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent("\u2500\u2500\u2500 \u6267\u884C\u60C5\u51B5 \u2500\u2500\u2500"), "fgDim")]) })
77752
+ ] }),
77753
+ slice.map((lineText, index2) => {
77754
+ const isUser = lineText.startsWith("> ");
77755
+ const isTool = lineText.startsWith("\u25B8 ");
77756
+ const isResult = lineText.startsWith(" \u2514\u2500 ") || lineText.startsWith("\u2514\u2500 ");
77757
+ const isTurn = lineText.startsWith("\u2500\u2500 ");
77758
+ const token = isUser ? "accent" : isTool ? "codeKeyword" : isResult ? "fgDim" : isTurn ? "fgDim" : "fg";
77759
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent(lineText), token)]) }, start + index2);
77760
+ })
77761
+ ]
77762
+ }
77763
+ );
77356
77764
  }
77357
77765
  if (error51 !== void 0 && error51 !== "") {
77358
77766
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
@@ -77360,7 +77768,7 @@ function AgentHubPane({
77360
77768
  {
77361
77769
  title: "\u5B50\u4EE3\u7406",
77362
77770
  error: error51,
77363
- footnote: "Esc \u5173\u95ED \xB7 \u53EF\u91CD\u8BD5"
77771
+ footnote: limits === void 0 ? "Esc \u5173\u95ED \xB7 \u53EF\u91CD\u8BD5" : `${limits} \xB7 Esc \u5173\u95ED \xB7 \u53EF\u91CD\u8BD5`
77364
77772
  }
77365
77773
  );
77366
77774
  }
@@ -77370,16 +77778,15 @@ function AgentHubPane({
77370
77778
  {
77371
77779
  title: "\u5B50\u4EE3\u7406",
77372
77780
  body: "\u6682\u65E0\u5B50\u4EE3\u7406",
77373
- footnote: "Esc \u5173\u95ED \xB7 \u6709\u8FD0\u884C\u4E2D\u7684\u5B50\u4EE3\u7406\u65F6\u518D\u6253\u5F00"
77781
+ footnote: limits === void 0 ? "Esc \u5173\u95ED \xB7 \u6709\u8FD0\u884C\u4E2D\u7684\u5B50\u4EE3\u7406\u65F6\u518D\u6253\u5F00" : `${limits} \xB7 Esc \u5173\u95ED`
77374
77782
  }
77375
77783
  );
77376
77784
  }
77377
- const clamped = Math.min(Math.max(selectedIndex, 0), rows.length - 1);
77378
77785
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
77379
77786
  OverlayShell,
77380
77787
  {
77381
77788
  title: "\u5B50\u4EE3\u7406",
77382
- footnote: "j/k \u9009\u62E9 \xB7 Enter \u67E5\u770B \xB7 Esc \u5173\u95ED",
77789
+ footnote: limits === void 0 ? "j/k \u9009\u62E9 \xB7 Enter \u67E5\u770B \xB7 Esc \u5173\u95ED" : `${limits} \xB7 j/k \u9009\u62E9 \xB7 Enter \u67E5\u770B \xB7 Esc \u5173\u95ED`,
77383
77790
  children: [
77384
77791
  rows.map((row, index2) => hubRow(row, index2 === clamped)),
77385
77792
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: paintRow([styled(escapeContent(aggregateLine(rows)), "fgDim")]) })
@@ -78350,10 +78757,37 @@ function WorkspacePane({
78350
78757
  state,
78351
78758
  maxCols
78352
78759
  }) {
78760
+ const { rows: windowRows } = use_window_size_default();
78353
78761
  if (!state.open) return null;
78354
78762
  if (state.error !== void 0) {
78355
78763
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(OverlayShell, { title: "\u5DE5\u4F5C\u533A", error: state.error, footnote: "Esc \u5173\u95ED" });
78356
78764
  }
78765
+ if (state.preview !== void 0) {
78766
+ const { name: name2, lines: fileLines, scrollOffset } = state.preview;
78767
+ const pageSize = Math.max(1, windowRows - 6);
78768
+ const total = fileLines.length;
78769
+ const start = Math.min(scrollOffset, Math.max(0, total - 1));
78770
+ const slice = fileLines.slice(start, start + pageSize);
78771
+ const gutterWidth = Math.max(3, String(start + slice.length).length);
78772
+ const previewRows = slice.map((line5, idx) => {
78773
+ const lineNum = start + idx + 1;
78774
+ const lineStr = String(lineNum).padStart(gutterWidth, " ");
78775
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Text, { wrap: "truncate", children: paintRow([
78776
+ styled(`${lineStr} \u2502 `, "fgDim"),
78777
+ styled(escapeContent(line5), "fg")
78778
+ ]) }, lineNum);
78779
+ });
78780
+ const title = `\u9884\u89C8: ${name2} (${String(start + 1)}-${String(Math.min(start + slice.length, total))}/${String(total)} \u884C)`;
78781
+ const footnote2 = "j/k \u6EDA\u52A8 \xB7 q/Esc \u8FD4\u56DE \xB7 i \u63D2\u5165\u8DEF\u5F84";
78782
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
78783
+ OverlayShell,
78784
+ {
78785
+ title,
78786
+ footnote: footnote2,
78787
+ children: previewRows
78788
+ }
78789
+ );
78790
+ }
78357
78791
  const footnote = state.editing ? "Enter \u89E3\u6790 \xB7 Esc \u53D6\u6D88" : state.nodes.length === 0 ? "e \u8F93\u5165\u8DEF\u5F84 \xB7 Esc \u5173\u95ED" : "j/k \u9009\u62E9 \xB7 Enter \u6253\u5F00 \xB7 e \u8DEF\u5F84 \xB7 Esc \u5173\u95ED";
78358
78792
  const rows = [];
78359
78793
  state.nodes.forEach((node2, index2) => {
@@ -79039,19 +79473,7 @@ function mapKeyEvent(state, key, keyInfo, commands, pane, search2, timeline = {
79039
79473
  return holdComposer(state, { kind: "plan-directory-apply" });
79040
79474
  }
79041
79475
  if (workspaceOpen) {
79042
- if (overlays.workspace?.selectedKind === "file") {
79043
- const caret = clampCaretIndex(state.text, state.caretIndex);
79044
- const path2 = overlays.workspace.selectedPath ?? "";
79045
- return {
79046
- kind: "dispatch",
79047
- action: { kind: "workspace-enter" },
79048
- text: state.text.slice(0, caret) + path2 + state.text.slice(caret),
79049
- commandQuery: void 0,
79050
- prefixG: false,
79051
- renaming: state.renaming,
79052
- caretIndex: caret + path2.length
79053
- };
79054
- }
79476
+ if (overlays.workspace?.preview === true) return { kind: "none" };
79055
79477
  return holdComposer(state, { kind: "workspace-enter" });
79056
79478
  }
79057
79479
  if (modelPane.open) {
@@ -79361,7 +79783,33 @@ function mapKeyEvent(state, key, keyInfo, commands, pane, search2, timeline = {
79361
79783
  if (key === "k" || keyInfo.upArrow) {
79362
79784
  return holdComposer(state, { kind: "workspace-move", delta: -1 });
79363
79785
  }
79364
- if (key === "e") {
79786
+ if (keyInfo.pageUp) {
79787
+ return holdComposer(state, { kind: "workspace-move", delta: -20 });
79788
+ }
79789
+ if (keyInfo.pageDown) {
79790
+ return holdComposer(state, { kind: "workspace-move", delta: 20 });
79791
+ }
79792
+ if (key === " ") {
79793
+ if (overlays.workspace?.preview === true) return { kind: "none" };
79794
+ return holdComposer(state, { kind: "workspace-enter" });
79795
+ }
79796
+ if (key === "q") {
79797
+ return holdComposer(state, { kind: "workspace-escape" });
79798
+ }
79799
+ if (key === "i") {
79800
+ const path2 = overlays.workspace?.selectedPath ?? "";
79801
+ const caret = clampCaretIndex(state.text, state.caretIndex);
79802
+ return {
79803
+ kind: "dispatch",
79804
+ action: { kind: "workspace-insert" },
79805
+ text: state.text.slice(0, caret) + path2 + state.text.slice(caret),
79806
+ commandQuery: void 0,
79807
+ prefixG: false,
79808
+ renaming: state.renaming,
79809
+ caretIndex: caret + path2.length
79810
+ };
79811
+ }
79812
+ if (key === "e" && overlays.workspace?.preview !== true) {
79365
79813
  const draft = overlays.workspace?.rootPath ?? "";
79366
79814
  return {
79367
79815
  kind: "dispatch",
@@ -79681,6 +80129,7 @@ function TuiLoop({
79681
80129
  const [planReviewOffset, setPlanReviewOffset] = (0, import_react39.useState)(0);
79682
80130
  const viewportSequenceRef = (0, import_react39.useRef)(0);
79683
80131
  const [viewportCommand, setViewportCommand] = (0, import_react39.useState)();
80132
+ const [topDividerLabel, setTopDividerLabel] = (0, import_react39.useState)();
79684
80133
  const issueViewportCommand = (0, import_react39.useCallback)((command) => {
79685
80134
  frameProbe?.beginMeasurement();
79686
80135
  viewportSequenceRef.current += 1;
@@ -80009,7 +80458,8 @@ function TuiLoop({
80009
80458
  editing: workspacePaneRef.current.editing,
80010
80459
  rootPath: workspacePaneRef.current.root,
80011
80460
  selectedKind: workspacePaneRef.current.nodes[workspacePaneRef.current.selectedIndex]?.kind,
80012
- selectedPath: workspacePaneRef.current.nodes[workspacePaneRef.current.selectedIndex]?.path
80461
+ selectedPath: workspacePaneRef.current.nodes[workspacePaneRef.current.selectedIndex]?.path,
80462
+ preview: workspacePaneRef.current.preview !== void 0
80013
80463
  },
80014
80464
  feedback: {
80015
80465
  open: feedbackPaneRef.current.open,
@@ -80385,7 +80835,8 @@ function TuiLoop({
80385
80835
  renderPolicy,
80386
80836
  frameMetrics,
80387
80837
  motionPaused: viewportMotionPaused,
80388
- mode: currentMode
80838
+ mode: currentMode,
80839
+ onStickyHeaderChange: setTopDividerLabel
80389
80840
  });
80390
80841
  const conversationColumns = conversationWidth(columns);
80391
80842
  const hudRows = [];
@@ -80486,7 +80937,11 @@ function TuiLoop({
80486
80937
  view: agentHubPane.view,
80487
80938
  error: agentHubPane.error,
80488
80939
  transcript: agentHubPane.transcript,
80489
- missing: agentHubPane.missing
80940
+ missing: agentHubPane.missing,
80941
+ liveContinuable: agentHubPane.liveContinuable,
80942
+ maxActiveSubagents: agentHubPane.maxActiveSubagents,
80943
+ maxDepth: agentHubPane.maxDepth,
80944
+ scrollOffset: agentHubPane.scrollOffset
80490
80945
  });
80491
80946
  } else if (planDirectoryPane.open) {
80492
80947
  content3 = (0, import_react39.createElement)(PlanDirectoryPane, {
@@ -80612,7 +81067,8 @@ function TuiLoop({
80612
81067
  badge: adaptiveInfoFooter === void 0 ? badge : shortenHomePath(controller.getCwd(), homedir()),
80613
81068
  status,
80614
81069
  children: content3,
80615
- input: inputSlot
81070
+ input: inputSlot,
81071
+ topDividerLabel
80616
81072
  });
80617
81073
  }
80618
81074
 
@@ -80911,14 +81367,26 @@ function formatTurnError(error51) {
80911
81367
  if (code2 === "MISSING_CREDENTIAL" || rawMessage.includes("DEEPSEEK_API_KEY") || rawMessage.includes("MISSING_CREDENTIAL") || rawMessage.includes("no API key")) {
80912
81368
  return '\u26A0\uFE0F **API \u5BC6\u94A5\u7F3A\u5931**\uFF1A\u672A\u68C0\u6D4B\u5230\u6709\u6548\u5BC6\u94A5\u3002\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C `export DEEPSEEK_API_KEY="sk-..."` \u6216\u8F93\u5165 `/key` \u547D\u4EE4\u8FDB\u884C\u914D\u7F6E\u3002';
80913
81369
  }
80914
- if (status === 401 || code2 === "AUTH" || rawMessage.includes("401") || rawMessage.toLowerCase().includes("authentication fails") || rawMessage.toLowerCase().includes("invalid api key") || rawMessage.toLowerCase().includes("unauthorized")) {
80915
- return `\u26A0\uFE0F **API \u8BA4\u8BC1\u5931\u8D25\uFF08HTTP 401\uFF09**\uFF1AAPI Key \u65E0\u6548\u6216\u672A\u6388\u6743\uFF08${rawMessage}\uFF09\u3002\u8BF7\u68C0\u67E5\u5BC6\u94A5\u662F\u5426\u6B63\u786E\uFF0C\u6216\u4F7F\u7528 \`/key\` \u91CD\u65B0\u914D\u7F6E\u3002`;
81370
+ if (rawMessage.includes("reading 'prepare'") || rawMessage.includes('reading "prepare"')) {
81371
+ return `\u26A0\uFE0F **\u5DE5\u5177\u8C03\u5EA6\u5931\u8D25**\uFF1A${rawMessage}\u3002\u8BF7\u65B0\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u540E\u518D\u8BD5\uFF1B\u4E0D\u8981\u5728\u8BA4\u8BC1\u5931\u8D25\u7684\u540C\u4E00\u56DE\u5408\u91CC\u7528\u300C\u7EE7\u7EED\u300D\u63A5\u7740\u8DD1\u5DE5\u5177\u3002`;
81372
+ }
81373
+ const lowered = rawMessage.toLowerCase();
81374
+ const antigravity = lowered.includes("antigravity") || rawMessage.includes("/anti");
81375
+ if (code2 === "TIMEOUT" || lowered.includes("timed out") || lowered.includes("timeout") || lowered.includes("stalled") || rawMessage.includes("ETIMEDOUT")) {
81376
+ return antigravity ? `\u26A0\uFE0F **Antigravity \u8BF7\u6C42\u8D85\u65F6**\uFF1A${rawMessage}\u3002\u8FD9\u662F\u94FE\u8DEF\u6216\u6D41\u8D85\u65F6\uFF0C\u4E0D\u662F\u767B\u5F55\u5931\u6548\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u4E0D\u5FC5\u6267\u884C \`/anti login\`\uFF0C\u4E5F\u4E0D\u8981\u4F7F\u7528 \`/key\`\u3002` : `\u26A0\uFE0F **\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25**\uFF1A\u65E0\u6CD5\u8FDE\u63A5\u5230\u6A21\u578B\u670D\u52A1\uFF08${rawMessage}\uFF09\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u6216\u4EE3\u7406\u914D\u7F6E\u3002`;
80916
81377
  }
80917
- if (status === 429 || code2 === "RATE_LIMIT" || rawMessage.includes("429") || rawMessage.toLowerCase().includes("quota") || rawMessage.toLowerCase().includes("rate limit")) {
81378
+ if (status === 429 || code2 === "RATE_LIMIT" || rawMessage.includes("429") || lowered.includes("quota") || lowered.includes("rate limit")) {
80918
81379
  return `\u26A0\uFE0F **\u8BF7\u6C42\u53D7\u9650\uFF08HTTP 429\uFF09**\uFF1A\u8BF7\u6C42\u9891\u7387\u8D85\u9650\u6216\u8D26\u6237\u4F59\u989D\u4E0D\u8DB3\uFF08${rawMessage}\uFF09\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u6216\u68C0\u67E5\u8D26\u6237\u989D\u5EA6\u3002`;
80919
81380
  }
80920
- if (rawMessage.includes("ECONNREFUSED") || rawMessage.includes("ETIMEDOUT") || rawMessage.includes("ENOTFOUND") || rawMessage.includes("fetch failed")) {
80921
- return `\u26A0\uFE0F **\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25**\uFF1A\u65E0\u6CD5\u8FDE\u63A5\u5230\u6A21\u578B\u670D\u52A1\uFF08${rawMessage}\uFF09\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u6216\u4EE3\u7406\u914D\u7F6E\u3002`;
81381
+ if (code2 === "NETWORK" || code2 === "TRANSPORT" || lowered.includes("could not be reached") || lowered.includes("proxy rejected") || rawMessage.includes("ECONNREFUSED") || rawMessage.includes("ENOTFOUND") || rawMessage.includes("fetch failed")) {
81382
+ return antigravity ? `\u26A0\uFE0F **Antigravity \u7F51\u7EDC\u4E0D\u53EF\u8FBE**\uFF1A${rawMessage}\u3002\u8FD9\u662F\u79C1\u6709\u7AEF\u70B9\u6216 HTTPS_PROXY \u8FDE\u4E0D\u4E0A\uFF0C\u4E0D\u662F\u767B\u5F55\u5931\u6548\u3002\u8BF7\u68C0\u67E5\u672C\u673A\u4EE3\u7406\u540E\u91CD\u8BD5\uFF1B\u4E0D\u5FC5\u6267\u884C \`/anti login\`\uFF0C\u4E5F\u4E0D\u8981\u4F7F\u7528 \`/key\`\u3002` : `\u26A0\uFE0F **\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25**\uFF1A\u65E0\u6CD5\u8FDE\u63A5\u5230\u6A21\u578B\u670D\u52A1\uFF08${rawMessage}\uFF09\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u6216\u4EE3\u7406\u914D\u7F6E\u3002`;
81383
+ }
81384
+ if (antigravity) {
81385
+ const authFailure = code2 === "AUTH" || lowered.includes("login is required") || lowered.includes("invalid grant") || lowered.includes("refresh token is invalid") || lowered.includes("no usable access token") || status === 401;
81386
+ return authFailure ? `\u26A0\uFE0F **Antigravity \u8BA4\u8BC1\u5931\u8D25**\uFF1A${rawMessage}\u3002\u8BF7\u4F7F\u7528 \`/anti status\` \u786E\u8BA4\u767B\u5F55\uFF0C\u5FC5\u8981\u65F6\u6267\u884C \`/anti login\` \u91CD\u65B0\u767B\u5F55\u3002Antigravity \u4F7F\u7528 Google OAuth\uFF0C\u4E0D\u662F API Key\uFF0C\u4E0D\u8981\u4F7F\u7528 \`/key\`\u3002` : `\u26A0\uFE0F **Antigravity \u8BF7\u6C42\u5931\u8D25**\uFF1A${rawMessage}\u3002\u82E5 \`/anti status\` \u663E\u793A\u5DF2\u767B\u5F55\uFF0C\u76F4\u63A5\u91CD\u8BD5\u5373\u53EF\uFF1B\u4E0D\u8981\u4F7F\u7528 \`/key\`\u3002`;
81387
+ }
81388
+ if (status === 401 || code2 === "AUTH" || rawMessage.includes("401") || lowered.includes("authentication fails") || lowered.includes("invalid api key") || lowered.includes("unauthorized")) {
81389
+ return `\u26A0\uFE0F **API \u8BA4\u8BC1\u5931\u8D25\uFF08HTTP 401\uFF09**\uFF1AAPI Key \u65E0\u6548\u6216\u672A\u6388\u6743\uFF08${rawMessage}\uFF09\u3002\u8BF7\u68C0\u67E5\u5BC6\u94A5\u662F\u5426\u6B63\u786E\uFF0C\u6216\u4F7F\u7528 \`/key\` \u91CD\u65B0\u914D\u7F6E\u3002`;
80922
81390
  }
80923
81391
  return `\u26A0\uFE0F **\u6A21\u578B\u8BF7\u6C42\u5931\u8D25**\uFF1A${rawMessage}`;
80924
81392
  }
@@ -81077,6 +81545,7 @@ function createProjector() {
81077
81545
  compactionDividers[index2] = deepFreeze({
81078
81546
  ...current,
81079
81547
  shadowedCount: event.data.shadowedSeqs.length,
81548
+ shadowedTokenCount: event.data.shadowedTokenCount,
81080
81549
  summary: event.data.summary.filter((block) => block.type === "text").map((block) => block.text).join("")
81081
81550
  });
81082
81551
  return;
@@ -81166,14 +81635,27 @@ ${formattedError}` });
81166
81635
  status = "idle";
81167
81636
  return;
81168
81637
  }
81169
- default:
81638
+ default: {
81639
+ if (event.type === "workspace/changes") {
81640
+ const turn = event.data.turn;
81641
+ if (typeof turn === "number") {
81642
+ for (let i = history.length - 1; i >= 0; i--) {
81643
+ const row = history[i];
81644
+ if (row !== void 0 && row.kind === "assistant" && row.turnOrdinal === turn) {
81645
+ history[i] = { ...row, workspaceChangesSeq: event.seq };
81646
+ break;
81647
+ }
81648
+ }
81649
+ }
81650
+ }
81170
81651
  return;
81652
+ }
81171
81653
  }
81172
81654
  }
81173
81655
  return {
81174
81656
  push: push3,
81175
81657
  seed(events) {
81176
- if (!events || typeof events[Symbol.iterator] !== "function") return;
81658
+ if (!events || !Array.isArray(events)) return;
81177
81659
  for (const event of events) push3(event);
81178
81660
  },
81179
81661
  snapshot() {
@@ -81454,7 +81936,8 @@ var LEADING_NAMESPACES = [
81454
81936
  "llm-deepseek",
81455
81937
  "llm-openai",
81456
81938
  "llm-anthropic",
81457
- "llm-pi-ai"
81939
+ "llm-pi-ai",
81940
+ "subagent"
81458
81941
  ];
81459
81942
  var BRAND_ANIMATION_LABELS = {
81460
81943
  auto: "\u81EA\u52A8",
@@ -82191,6 +82674,9 @@ function transitionStatusText(state) {
82191
82674
  function errorReason(error51) {
82192
82675
  return error51 instanceof Error ? error51.message : String(error51);
82193
82676
  }
82677
+ function terminalErrorReason(error51) {
82678
+ return userErrorReason(error51);
82679
+ }
82194
82680
  function fieldOf(section, field) {
82195
82681
  if (typeof section !== "object" || section === null || Array.isArray(section)) {
82196
82682
  return void 0;
@@ -82296,6 +82782,18 @@ function hubTranscriptOf(events) {
82296
82782
  if (text5 !== "") lines.push(`> ${text5}`);
82297
82783
  continue;
82298
82784
  }
82785
+ if (event.type === "tool/call") {
82786
+ const args = typeof event.data.arguments === "string" ? event.data.arguments : JSON.stringify(event.data.arguments);
82787
+ const shortArgs = args.length > 80 ? `${args.slice(0, 77)}...` : args;
82788
+ lines.push(`\u25B8 ${event.data.name} ${shortArgs}`);
82789
+ continue;
82790
+ }
82791
+ if (event.type === "tool/result") {
82792
+ const text5 = joinTextBlocks(event.data.message.content, "");
82793
+ const shortText = text5.length > 80 ? `${text5.slice(0, 77).replace(/\n/g, " ")}...` : text5.replace(/\n/g, " ");
82794
+ if (shortText !== "") lines.push(` \u2514\u2500 ${shortText}`);
82795
+ continue;
82796
+ }
82299
82797
  if (event.type !== "assistant/message") continue;
82300
82798
  const text4 = joinTextBlocks(event.data.message.content, "");
82301
82799
  if (text4 !== "") lines.push(`\u25CF ${text4}`);
@@ -82308,6 +82806,7 @@ var SEARCH_LIMIT = 30;
82308
82806
  var SEARCH_DEBOUNCE_MS = 120;
82309
82807
  var LOCAL_COMMANDS = [
82310
82808
  { name: "export", description: "Export this session to a Markdown file" },
82809
+ { name: "files", description: "Browse and preview workspace files" },
82311
82810
  { name: "help", description: "Show command help and key bindings" },
82312
82811
  { name: "key", description: "Set or update API key", input: { hint: "[KEY_NAME] <SECRET>" } },
82313
82812
  { name: "login", description: "Set or update API key", input: { hint: "[KEY_NAME] <SECRET>" } },
@@ -82338,6 +82837,10 @@ var RuntimeController = class _RuntimeController {
82338
82837
  this.disposeSessionEvents = ctx.on("session/event", (session, event) => {
82339
82838
  if (session !== this.liveHandle?.agent.session) return;
82340
82839
  this.projector.push(event);
82840
+ if (event.type === "workspace/changes") this.cacheWorkspaceChange(event.seq);
82841
+ if (this.agentHubOpen && (event.type === "subagent/catalog" || event.type === "subagent/descriptor")) {
82842
+ this.ownWork(this.refreshAgentHub(), "agent hub list");
82843
+ }
82341
82844
  if (event.type === "turn/start") {
82342
82845
  this.reduce({ kind: "turn-started" }, void 0, void 0);
82343
82846
  } else if (event.type === "llm/retry") {
@@ -82490,12 +82993,17 @@ var RuntimeController = class _RuntimeController {
82490
82993
  /** Idempotent quiescent controller teardown. */
82491
82994
  disposeInFlight;
82492
82995
  closed = false;
82996
+ isClosed() {
82997
+ return this.closed;
82998
+ }
82493
82999
  machine = "idle";
82494
83000
  /** Durable retry wait currently visible in the footer. */
82495
83001
  retryFooter;
82496
83002
  /** Single bounded refresh timer while {@link retryFooter} is visible. */
82497
83003
  retryFooterTimer;
82498
83004
  projector = createProjector();
83005
+ /** Host `workspace/changes` cards keyed by announcing event sequence. */
83006
+ workspaceChangeCards = /* @__PURE__ */ new Map();
82499
83007
  presentationOverrides = /* @__PURE__ */ new Map();
82500
83008
  presentationWrites = Promise.resolve();
82501
83009
  toolCardsExpanded = false;
@@ -82580,6 +83088,7 @@ var RuntimeController = class _RuntimeController {
82580
83088
  agentHubView = "table";
82581
83089
  agentHubError;
82582
83090
  agentHubTranscript;
83091
+ agentHubScrollOffset = 0;
82583
83092
  /** Monotonic listing/inspect id: only the newest Hub request may land. */
82584
83093
  agentHubSeq = 0;
82585
83094
  /** Cancellation owner for the newest Hub list, fold, expansion, or inspect. */
@@ -82604,6 +83113,7 @@ var RuntimeController = class _RuntimeController {
82604
83113
  workspaceResolveError;
82605
83114
  /** Stale guard for in-flight workspace loads; bumped on open/close/re-root. */
82606
83115
  workspaceSeq = 0;
83116
+ workspacePreview = void 0;
82607
83117
  feedbackOpen = false;
82608
83118
  /** True while the feedback note draft owns the composer. */
82609
83119
  feedbackEditing = false;
@@ -82779,6 +83289,7 @@ var RuntimeController = class _RuntimeController {
82779
83289
  const model = this.projector.snapshot();
82780
83290
  this.modelSnapshot = {
82781
83291
  ...model,
83292
+ history: this.decorateWorkspaceChanges(model.history),
82782
83293
  reasoningExpanded: this.presentationFlag("reasoning"),
82783
83294
  toolCardsExpanded: this.toolCardsExpanded,
82784
83295
  expandedCompactionId: this.expandedCompactionId,
@@ -82787,6 +83298,55 @@ var RuntimeController = class _RuntimeController {
82787
83298
  }
82788
83299
  return this.modelSnapshot;
82789
83300
  }
83301
+ /**
83302
+ * Attach cached Host summaries onto frozen assistant turns.
83303
+ * @param history - projector history.
83304
+ * @returns history with `workspaceChanges` filled when a card exists.
83305
+ */
83306
+ decorateWorkspaceChanges(history) {
83307
+ if (this.workspaceChangeCards.size === 0) return history;
83308
+ return history.map((row) => {
83309
+ if (row.kind !== "assistant" || row.workspaceChangesSeq === void 0) return row;
83310
+ const card = this.workspaceChangeCards.get(row.workspaceChangesSeq);
83311
+ return card === void 0 ? row : { ...row, workspaceChanges: card };
83312
+ });
83313
+ }
83314
+ /**
83315
+ * Read one announcing event's Host summary into the transcript card cache.
83316
+ * Empty or abandoned records drop any earlier card for that sequence.
83317
+ * @param seq - `workspace/changes` event sequence.
83318
+ */
83319
+ cacheWorkspaceChange(seq) {
83320
+ const sessionId = this.session?.id;
83321
+ const service = this.ctx.get("workspaceChanges");
83322
+ const summary = sessionId === void 0 || service === void 0 ? void 0 : service.summary(sessionId, seq);
83323
+ const card = workspaceChangesCardFromSummary(seq, summary);
83324
+ if (card === void 0) this.workspaceChangeCards.delete(seq);
83325
+ else this.workspaceChangeCards.set(seq, card);
83326
+ }
83327
+ /** Replay Host summaries for every `workspace/changes` event on the bound Session. */
83328
+ hydrateWorkspaceChangeCards() {
83329
+ this.workspaceChangeCards.clear();
83330
+ const session = this.session;
83331
+ if (session === void 0) return;
83332
+ for (const event of getSessionEvents(session)) {
83333
+ if (event.type === "workspace/changes") this.cacheWorkspaceChange(event.seq);
83334
+ }
83335
+ }
83336
+ /**
83337
+ * Continuable defaults from live settings, never a stale Hub cache.
83338
+ * @returns max live children (8) and max depth (1) unless the user raised them.
83339
+ */
83340
+ subagentLimits() {
83341
+ const settings = this.ctx.get("settings");
83342
+ const section = settings === void 0 ? void 0 : settings.get("subagent");
83343
+ const maxActive = fieldOf(section, "maxActiveSubagents");
83344
+ const maxDepth = fieldOf(section, "maxDepth");
83345
+ return {
83346
+ maxActiveSubagents: typeof maxActive === "number" ? maxActive : 8,
83347
+ maxDepth: typeof maxDepth === "number" ? maxDepth : 1
83348
+ };
83349
+ }
82790
83350
  /**
82791
83351
  * Return the interaction state.
82792
83352
  * @returns the current interaction state.
@@ -83034,17 +83594,25 @@ var RuntimeController = class _RuntimeController {
83034
83594
  */
83035
83595
  getAgentHubPane() {
83036
83596
  if (!this.agentHubOpen) return EMPTY_OVERLAY_PANE;
83037
- if (this.agentHubPaneSnapshot === void 0) {
83038
- this.agentHubPaneSnapshot = {
83039
- open: true,
83040
- rows: this.agentHubRows,
83041
- selectedIndex: this.agentHubSelectedIndex,
83042
- view: this.agentHubView,
83043
- missing: this.ctx.get("subagents") === void 0,
83044
- ...this.agentHubError === void 0 ? {} : { error: this.agentHubError },
83045
- ...this.agentHubTranscript === void 0 ? {} : { transcript: this.agentHubTranscript }
83046
- };
83597
+ const limits = this.subagentLimits();
83598
+ const liveContinuable = this.agentHubRows.filter((row) => row.activity === "running").length;
83599
+ const cached2 = this.agentHubPaneSnapshot;
83600
+ if (cached2 !== void 0 && cached2.rows === this.agentHubRows && cached2.selectedIndex === this.agentHubSelectedIndex && cached2.view === this.agentHubView && cached2.error === this.agentHubError && cached2.transcript === this.agentHubTranscript && cached2.scrollOffset === this.agentHubScrollOffset && cached2.liveContinuable === liveContinuable && cached2.maxActiveSubagents === limits.maxActiveSubagents && cached2.maxDepth === limits.maxDepth) {
83601
+ return cached2;
83047
83602
  }
83603
+ this.agentHubPaneSnapshot = {
83604
+ open: true,
83605
+ rows: this.agentHubRows,
83606
+ selectedIndex: this.agentHubSelectedIndex,
83607
+ view: this.agentHubView,
83608
+ missing: this.ctx.get("subagents") === void 0,
83609
+ liveContinuable,
83610
+ scrollOffset: this.agentHubScrollOffset,
83611
+ maxActiveSubagents: limits.maxActiveSubagents,
83612
+ maxDepth: limits.maxDepth,
83613
+ ...this.agentHubError === void 0 ? {} : { error: this.agentHubError },
83614
+ ...this.agentHubTranscript === void 0 ? {} : { transcript: this.agentHubTranscript }
83615
+ };
83048
83616
  return this.agentHubPaneSnapshot;
83049
83617
  }
83050
83618
  /**
@@ -83086,7 +83654,8 @@ var RuntimeController = class _RuntimeController {
83086
83654
  selectedIndex: this.workspaceSelectedIndex,
83087
83655
  editing: this.workspaceEditing,
83088
83656
  error: fs3 === void 0 ? "\u6587\u4EF6\u7CFB\u7EDF\u672A\u7EC4\u5408" : void 0,
83089
- resolveError: this.workspaceResolveError
83657
+ resolveError: this.workspaceResolveError,
83658
+ preview: this.workspacePreview
83090
83659
  };
83091
83660
  }
83092
83661
  return this.workspacePaneSnapshot;
@@ -83177,6 +83746,57 @@ var RuntimeController = class _RuntimeController {
83177
83746
  this.workspaceSelectedIndex = 0;
83178
83747
  this.workspaceResolveError = void 0;
83179
83748
  }
83749
+ /** Load a workspace file into the inline previewer. */
83750
+ async openWorkspacePreview(row) {
83751
+ const fs3 = this.ctx.get("fs");
83752
+ if (fs3 === void 0) return;
83753
+ const seq = this.workspaceSeq;
83754
+ try {
83755
+ const change = lookupWorkspaceChange(
83756
+ this.workspaceChangeCards,
83757
+ row.target.displayPath
83758
+ ) ?? lookupWorkspaceChange(this.workspaceChangeCards, row.name);
83759
+ if (change !== void 0) {
83760
+ const sessionId = this.session?.id;
83761
+ const service = this.ctx.get("workspaceChanges");
83762
+ if (sessionId === void 0 || service === void 0) {
83763
+ this.setFeedback(`\u2717 ${SESSION_CHANGES_DISPOSED_COPY}`);
83764
+ return;
83765
+ }
83766
+ const diff2 = await service.diff(sessionId, change.seq, change.index, this.lifecycleAbort.signal);
83767
+ if (seq !== this.workspaceSeq || !this.workspaceOpen) return;
83768
+ const preview = formatWorkspaceChangeDiffPreview(diff2);
83769
+ if (preview === void 0) {
83770
+ this.setFeedback(`\u2717 ${SESSION_CHANGES_DISPOSED_COPY}`);
83771
+ return;
83772
+ }
83773
+ this.workspacePreview = {
83774
+ path: row.target.displayPath,
83775
+ name: row.name,
83776
+ lines: [...preview],
83777
+ scrollOffset: 0
83778
+ };
83779
+ this.workspacePaneSnapshot = void 0;
83780
+ this.emit();
83781
+ return;
83782
+ }
83783
+ const text4 = await fs3.readText(row.target);
83784
+ if (seq !== this.workspaceSeq || !this.workspaceOpen) return;
83785
+ const lines = text4.split(/\r?\n/u);
83786
+ const capped = lines.length > 5e3 ? [...lines.slice(0, 5e3), "\u2026 (truncated)"] : lines;
83787
+ this.workspacePreview = {
83788
+ path: row.target.displayPath,
83789
+ name: row.name,
83790
+ lines: capped,
83791
+ scrollOffset: 0
83792
+ };
83793
+ this.workspacePaneSnapshot = void 0;
83794
+ this.emit();
83795
+ } catch (error51) {
83796
+ if (seq !== this.workspaceSeq || !this.workspaceOpen) return;
83797
+ this.setFeedback(`\u2717 \u65E0\u6CD5\u9884\u89C8\u6587\u4EF6\uFF1A${errorReason(error51)}`);
83798
+ }
83799
+ }
83180
83800
  /**
83181
83801
  * Return the message-feedback overlay targeting the assistant message that
83182
83802
  * was latest when the overlay opened. Paints the S19 opener error when the
@@ -83712,7 +84332,9 @@ var RuntimeController = class _RuntimeController {
83712
84332
  this.agentHubView = "table";
83713
84333
  this.agentHubError = void 0;
83714
84334
  this.agentHubTranscript = void 0;
84335
+ this.agentHubScrollOffset = 0;
83715
84336
  this.agentHubSeq += 1;
84337
+ this.agentHubPaneSnapshot = void 0;
83716
84338
  }
83717
84339
  /**
83718
84340
  * Cancel the prior Hub read and start one request tied to controller teardown.
@@ -83739,6 +84361,7 @@ var RuntimeController = class _RuntimeController {
83739
84361
  this.agentHubError = void 0;
83740
84362
  this.agentHubRows = [];
83741
84363
  this.agentHubSelectedIndex = 0;
84364
+ this.agentHubPaneSnapshot = void 0;
83742
84365
  this.emit();
83743
84366
  const subagents = this.ctx.get("subagents");
83744
84367
  if (subagents === void 0 || this.session === void 0) return;
@@ -83748,11 +84371,14 @@ var RuntimeController = class _RuntimeController {
83748
84371
  if (!this.agentHubOpen || seq !== this.agentHubSeq) return;
83749
84372
  this.agentHubRows = rows;
83750
84373
  this.agentHubError = void 0;
84374
+ this.agentHubPaneSnapshot = void 0;
84375
+ this.agentHubError = void 0;
83751
84376
  this.emit();
83752
84377
  } catch (error51) {
83753
84378
  if (!this.agentHubOpen || seq !== this.agentHubSeq) return;
83754
84379
  this.agentHubRows = [];
83755
84380
  this.agentHubError = `\u65E0\u6CD5\u5217\u51FA\u5B50\u4EE3\u7406\uFF1A${errorReason(error51)}`;
84381
+ this.agentHubPaneSnapshot = void 0;
83756
84382
  this.emit();
83757
84383
  }
83758
84384
  }
@@ -83771,10 +84397,12 @@ var RuntimeController = class _RuntimeController {
83771
84397
  this.agentHubRows = rows;
83772
84398
  this.agentHubSelectedIndex = 0;
83773
84399
  this.agentHubError = void 0;
84400
+ this.agentHubPaneSnapshot = void 0;
83774
84401
  this.emit();
83775
84402
  } catch (error51) {
83776
84403
  if (!this.agentHubOpen || seq !== this.agentHubSeq) return;
83777
84404
  this.agentHubError = `\u65E0\u6CD5\u5217\u51FA\u5B50\u4EE3\u7406\uFF1A${errorReason(error51)}`;
84405
+ this.agentHubPaneSnapshot = void 0;
83778
84406
  this.emit();
83779
84407
  }
83780
84408
  }
@@ -83862,6 +84490,8 @@ var RuntimeController = class _RuntimeController {
83862
84490
  this.agentHubView = "transcript";
83863
84491
  this.agentHubTranscript = void 0;
83864
84492
  this.agentHubError = void 0;
84493
+ this.agentHubScrollOffset = 0;
84494
+ this.agentHubPaneSnapshot = void 0;
83865
84495
  this.emit();
83866
84496
  const persistence = this.ctx.get("sessionPersistence");
83867
84497
  if (persistence === void 0) {
@@ -83886,10 +84516,13 @@ var RuntimeController = class _RuntimeController {
83886
84516
  if (!this.agentHubOpen || seq !== this.agentHubSeq) return;
83887
84517
  this.agentHubTranscript = hubTranscriptOf(events);
83888
84518
  this.agentHubError = void 0;
84519
+ this.agentHubPaneSnapshot = void 0;
84520
+ this.agentHubError = void 0;
83889
84521
  this.emit();
83890
84522
  } catch (error51) {
83891
84523
  if (!this.agentHubOpen || seq !== this.agentHubSeq) return;
83892
84524
  this.agentHubError = `\u65E0\u6CD5\u8BFB\u53D6\u5B50\u4F1A\u8BDD\uFF1A${errorReason(error51)}`;
84525
+ this.agentHubPaneSnapshot = void 0;
83893
84526
  this.emit();
83894
84527
  }
83895
84528
  }
@@ -84205,6 +84838,8 @@ var RuntimeController = class _RuntimeController {
84205
84838
  this.agentHubView = "table";
84206
84839
  this.agentHubTranscript = void 0;
84207
84840
  this.agentHubError = void 0;
84841
+ this.agentHubScrollOffset = 0;
84842
+ this.agentHubPaneSnapshot = void 0;
84208
84843
  this.emit();
84209
84844
  return;
84210
84845
  }
@@ -84213,12 +84848,19 @@ var RuntimeController = class _RuntimeController {
84213
84848
  this.emit();
84214
84849
  return;
84215
84850
  case "agent-hub-move":
84216
- if (!this.agentHubOpen || this.agentHubView !== "table") return;
84851
+ if (!this.agentHubOpen) return;
84852
+ if (this.agentHubView === "transcript") {
84853
+ this.agentHubScrollOffset = Math.max(0, this.agentHubScrollOffset + action.delta);
84854
+ this.agentHubPaneSnapshot = void 0;
84855
+ this.emit();
84856
+ return;
84857
+ }
84217
84858
  if (this.agentHubRows.length === 0) return;
84218
84859
  this.agentHubSelectedIndex = Math.min(
84219
84860
  this.agentHubRows.length - 1,
84220
84861
  Math.max(0, this.agentHubSelectedIndex + action.delta)
84221
84862
  );
84863
+ this.agentHubPaneSnapshot = void 0;
84222
84864
  this.emit();
84223
84865
  return;
84224
84866
  case "agent-hub-enter":
@@ -84235,6 +84877,7 @@ var RuntimeController = class _RuntimeController {
84235
84877
  return;
84236
84878
  case "workspace-pane": {
84237
84879
  const opening = !this.workspaceOpen;
84880
+ this.workspacePreview = void 0;
84238
84881
  this.toggleOverlay("workspaceOpen");
84239
84882
  if (opening) {
84240
84883
  this.workspaceSeq += 1;
@@ -84247,12 +84890,26 @@ var RuntimeController = class _RuntimeController {
84247
84890
  }
84248
84891
  case "workspace-escape":
84249
84892
  if (!this.workspaceOpen) return;
84893
+ if (this.workspacePreview !== void 0) {
84894
+ this.workspacePreview = void 0;
84895
+ this.workspacePaneSnapshot = void 0;
84896
+ this.emit();
84897
+ return;
84898
+ }
84250
84899
  this.workspaceOpen = false;
84251
84900
  this.workspaceSeq += 1;
84252
84901
  this.emit();
84253
84902
  return;
84254
84903
  case "workspace-move": {
84255
84904
  if (!this.workspaceOpen || this.workspaceEditing) return;
84905
+ if (this.workspacePreview !== void 0) {
84906
+ const maxOffset2 = Math.max(0, this.workspacePreview.lines.length - 1);
84907
+ const nextOffset = Math.min(maxOffset2, Math.max(0, this.workspacePreview.scrollOffset + action.delta));
84908
+ this.workspacePreview = { ...this.workspacePreview, scrollOffset: nextOffset };
84909
+ this.workspacePaneSnapshot = void 0;
84910
+ this.emit();
84911
+ return;
84912
+ }
84256
84913
  const rowCount = this.computeWorkspaceRows().length;
84257
84914
  this.workspaceSelectedIndex = Math.min(
84258
84915
  Math.max(0, rowCount - 1),
@@ -84263,6 +84920,7 @@ var RuntimeController = class _RuntimeController {
84263
84920
  }
84264
84921
  case "workspace-enter": {
84265
84922
  if (!this.workspaceOpen || this.workspaceEditing) return;
84923
+ if (this.workspacePreview !== void 0) return;
84266
84924
  const row = this.computeWorkspaceRows()[this.workspaceSelectedIndex];
84267
84925
  if (row === void 0) return;
84268
84926
  if (row.kind === "directory") {
@@ -84270,12 +84928,20 @@ var RuntimeController = class _RuntimeController {
84270
84928
  return;
84271
84929
  }
84272
84930
  if (row.kind === "file") {
84273
- this.workspaceOpen = false;
84274
- this.workspaceSeq += 1;
84275
- this.emit();
84931
+ this.ownWork(this.openWorkspacePreview(row), "workspace file preview");
84276
84932
  }
84277
84933
  return;
84278
84934
  }
84935
+ case "workspace-insert": {
84936
+ if (!this.workspaceOpen) return;
84937
+ this.workspaceOpen = false;
84938
+ this.workspacePreview = void 0;
84939
+ this.workspaceEditing = false;
84940
+ this.workspaceSeq += 1;
84941
+ this.workspacePaneSnapshot = void 0;
84942
+ this.emit();
84943
+ return;
84944
+ }
84279
84945
  case "workspace-edit":
84280
84946
  if (!this.workspaceOpen) return;
84281
84947
  this.workspaceEditing = true;
@@ -84974,7 +85640,7 @@ var RuntimeController = class _RuntimeController {
84974
85640
  } catch (error51) {
84975
85641
  fail(
84976
85642
  this.io,
84977
- new Error(`cannot resume session "${resumeId}": ${errorReason(error51)}`)
85643
+ new Error(`cannot resume session "${resumeId}": ${terminalErrorReason(error51)}`)
84978
85644
  );
84979
85645
  }
84980
85646
  }
@@ -85051,9 +85717,13 @@ var RuntimeController = class _RuntimeController {
85051
85717
  this.ctx.logger.warn(
85052
85718
  `session ${request.intent} failed: ${errorReason(primaryError)}`
85053
85719
  );
85054
- this.setFeedback(
85055
- request.intent === "create" ? "\u2717 \u65B0\u5EFA\u4F1A\u8BDD\u5931\u8D25\uFF08\u5F53\u524D\u4F1A\u8BDD\u4FDD\u6301\u53EF\u7528\uFF09" : `\u2717 \u5207\u6362\u5931\u8D25\uFF1A${errorReason(primaryError)}\uFF08\u5F53\u524D\u4F1A\u8BDD\u4FDD\u6301\u53EF\u7528\uFF09`
85056
- );
85720
+ if (request.intent === "create") {
85721
+ this.setFeedback(
85722
+ isSessionWriterHeld(primaryError) ? `\u2717 \u65B0\u5EFA\u4F1A\u8BDD\u5931\u8D25\uFF1A${SESSION_WRITER_HELD_COPY}\uFF08\u5F53\u524D\u4F1A\u8BDD\u4FDD\u6301\u53EF\u7528\uFF09` : "\u2717 \u65B0\u5EFA\u4F1A\u8BDD\u5931\u8D25\uFF08\u5F53\u524D\u4F1A\u8BDD\u4FDD\u6301\u53EF\u7528\uFF09"
85723
+ );
85724
+ } else {
85725
+ this.setFeedback(`\u2717 \u5207\u6362\u5931\u8D25\uFF1A${terminalErrorReason(primaryError)}\uFF08\u5F53\u524D\u4F1A\u8BDD\u4FDD\u6301\u53EF\u7528\uFF09`);
85726
+ }
85057
85727
  return;
85058
85728
  }
85059
85729
  if (previous3.liveHandle !== void 0) {
@@ -85066,17 +85736,6 @@ var RuntimeController = class _RuntimeController {
85066
85736
  }
85067
85737
  }
85068
85738
  if (previous3.session !== void 0) {
85069
- const events = getSessionEvents(previous3.session);
85070
- if (isSessionEmpty(events)) {
85071
- const persistence = this.ctx.get("sessionPersistence");
85072
- if (persistence !== void 0 && typeof persistence.delete === "function") {
85073
- try {
85074
- await persistence.delete(previous3.session.id);
85075
- } catch (error51) {
85076
- this.ctx.logger.warn(`failed to delete empty session "${previous3.session.id}": ${errorReason(error51)}`);
85077
- }
85078
- }
85079
- }
85080
85739
  this.sessionRowCache.delete(previous3.session.id);
85081
85740
  }
85082
85741
  if (this.session !== void 0) {
@@ -85170,6 +85829,7 @@ var RuntimeController = class _RuntimeController {
85170
85829
  };
85171
85830
  this.session = agent.session;
85172
85831
  this.projector = candidate.projector;
85832
+ this.hydrateWorkspaceChangeCards();
85173
85833
  this.modelSelectionRef = candidate.modelSelectionRef;
85174
85834
  this.badge = candidate.badge;
85175
85835
  this.machine = "idle";
@@ -85387,6 +86047,12 @@ var RuntimeController = class _RuntimeController {
85387
86047
  this.togglePresentationFlag(query === "status" ? "statusDetails" : query);
85388
86048
  return;
85389
86049
  }
86050
+ if (query === "files" || query === "files " || query === "workspace" || query === "workspace ") {
86051
+ if (this.blockingHead() !== void 0) return;
86052
+ this.closeOtherPanels();
86053
+ this.dispatch({ kind: "workspace-pane" });
86054
+ return;
86055
+ }
85390
86056
  if (query === "export") {
85391
86057
  this.ownWork(this.exportLive(), "session export");
85392
86058
  return;
@@ -86155,7 +86821,7 @@ var RuntimeController = class _RuntimeController {
86155
86821
  "g a \u5B50\u4EE3\u7406 \xB7 g t \u5DE5\u4F5C\u533A \xB7 g f \u53CD\u9988 \xB7 g w \u5DE5\u4F5C\u6D41",
86156
86822
  "\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",
86157
86823
  "\u6EDA\u8F6E\u6EDA\u52A8 \xB7 \u70B9\u51FB\u6253\u5F00\u94FE\u63A5 \xB7 \u62D6\u9009\u590D\u5236",
86158
- "/plan \u8BA1\u5212 \xB7 /goal \u76EE\u6807 \xB7 /compact \u538B\u7F29",
86824
+ "/files \u5DE5\u4F5C\u533A \xB7 /plan \u8BA1\u5212 \xB7 /goal \u76EE\u6807 \xB7 /compact \u538B\u7F29",
86159
86825
  "/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"
86160
86826
  ];
86161
86827
  }
@@ -86506,7 +87172,7 @@ var RuntimeController = class _RuntimeController {
86506
87172
  if (uncached.length > 0) {
86507
87173
  const BATCH_SIZE = 16;
86508
87174
  for (let i = 0; i < uncached.length; i += BATCH_SIZE) {
86509
- if (this.closed) return;
87175
+ if (this.isClosed()) return;
86510
87176
  const batch = uncached.slice(i, i + BATCH_SIZE);
86511
87177
  const fetched = await Promise.all(
86512
87178
  batch.map(async ({ id, revision }) => {
@@ -86517,7 +87183,7 @@ var RuntimeController = class _RuntimeController {
86517
87183
  for (const entry of fetched) {
86518
87184
  this.sessionRowCache.set(entry.id, { row: entry.row, revision: entry.revision, isEmpty: entry.isEmpty });
86519
87185
  }
86520
- if (!this.closed) {
87186
+ if (!this.isClosed()) {
86521
87187
  const updatedRows = [];
86522
87188
  for (const item of interactiveItems) {
86523
87189
  const header = item.header ?? item;
@@ -86605,7 +87271,7 @@ var RuntimeController = class _RuntimeController {
86605
87271
  id,
86606
87272
  title: listTitleOf(events),
86607
87273
  updatedAt: events.at(-1)?.time ?? createdAt,
86608
- isEmpty: isSessionEmpty(events)
87274
+ isEmpty: isSessionEmpty(events, id)
86609
87275
  };
86610
87276
  } catch {
86611
87277
  return { id, title: id, updatedAt: 0, isEmpty: true };
@@ -87113,10 +87779,11 @@ function isBootstrapOnlySession(events) {
87113
87779
  if (!Array.isArray(events) || events.length === 0) return false;
87114
87780
  return events.every((event) => typeof event.type === "string" && BOOTSTRAP_ONLY_TYPES.has(event.type));
87115
87781
  }
87116
- function isSessionEmpty(events) {
87117
- if (!Array.isArray(events) || events.length === 0) return false;
87118
- if (isBootstrapOnlySession(events)) return true;
87119
- return !events.some(isHumanUserMessage) && foldTitle(events) === void 0;
87782
+ function isSessionEmpty(events, id) {
87783
+ if (id !== void 0 && id.includes("probe")) {
87784
+ return isBootstrapOnlySession(events);
87785
+ }
87786
+ return false;
87120
87787
  }
87121
87788
  function planReviewOf(questions) {
87122
87789
  if (questions.length !== 1) return void 0;