@agi-fans/dsh-tui 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/lib/CHANGELOG.md +18 -1
  2. package/lib/chrome/tool-args.d.ts +9 -0
  3. package/lib/chrome/tool-args.js +20 -0
  4. package/lib/chrome/tool-args.js.map +1 -0
  5. package/lib/chrome/tool-renderers.js +4 -13
  6. package/lib/chrome/tool-renderers.js.map +1 -1
  7. package/lib/commands/session.js +2 -4
  8. package/lib/commands/session.js.map +1 -1
  9. package/lib/runtime/composer-images.d.ts +44 -0
  10. package/lib/runtime/composer-images.js +149 -0
  11. package/lib/runtime/composer-images.js.map +1 -0
  12. package/lib/runtime/herdr-agent.d.ts +95 -0
  13. package/lib/runtime/herdr-agent.js +258 -0
  14. package/lib/runtime/herdr-agent.js.map +1 -0
  15. package/lib/runtime/plain-tui.d.ts +34 -0
  16. package/lib/runtime/plain-tui.js +119 -0
  17. package/lib/runtime/plain-tui.js.map +1 -0
  18. package/lib/runtime/provider-local.d.ts +3 -0
  19. package/lib/runtime/provider-local.js +107 -268
  20. package/lib/runtime/provider-local.js.map +1 -1
  21. package/lib/runtime/tool-presentation.js +15 -0
  22. package/lib/runtime/tool-presentation.js.map +1 -1
  23. package/lib/session/content-text.d.ts +12 -0
  24. package/lib/session/content-text.js +13 -0
  25. package/lib/session/content-text.js.map +1 -0
  26. package/lib/session/history-store.js.map +1 -0
  27. package/lib/session/json-file.d.ts +5 -0
  28. package/lib/session/json-file.js +21 -0
  29. package/lib/session/json-file.js.map +1 -0
  30. package/lib/session/model-favorites.js +15 -25
  31. package/lib/session/model-favorites.js.map +1 -1
  32. package/lib/session/release-notes.js +1 -18
  33. package/lib/session/release-notes.js.map +1 -1
  34. package/lib/session/session-controller.js +64 -140
  35. package/lib/session/session-controller.js.map +1 -1
  36. package/lib/session/session-library.js +4 -14
  37. package/lib/session/session-library.js.map +1 -1
  38. package/lib/session/subagent-roster.js +13 -43
  39. package/lib/session/subagent-roster.js.map +1 -1
  40. package/lib/session/subagent-tracker.d.ts +58 -0
  41. package/lib/session/subagent-tracker.js +129 -0
  42. package/lib/session/subagent-tracker.js.map +1 -0
  43. package/lib/session/update-check.js +2 -15
  44. package/lib/session/update-check.js.map +1 -1
  45. package/lib/session/version-compare.d.ts +3 -0
  46. package/lib/session/version-compare.js +21 -0
  47. package/lib/session/version-compare.js.map +1 -0
  48. package/lib/views/event-views.d.ts +11 -270
  49. package/lib/views/event-views.js +12 -964
  50. package/lib/views/event-views.js.map +1 -1
  51. package/lib/views/settings-list.js +12 -11
  52. package/lib/views/settings-list.js.map +1 -1
  53. package/lib/views/transcript-render.d.ts +179 -0
  54. package/lib/views/transcript-render.js +916 -0
  55. package/lib/views/transcript-render.js.map +1 -0
  56. package/lib/views/transcript-types.d.ts +104 -0
  57. package/lib/views/transcript-types.js +53 -0
  58. package/lib/views/transcript-types.js.map +1 -0
  59. package/package.json +2 -1
  60. package/lib/views/history-store.js.map +0 -1
  61. /package/lib/{views → session}/history-store.d.ts +0 -0
  62. /package/lib/{views → session}/history-store.js +0 -0
@@ -1,6 +1,5 @@
1
1
  /** Durable, product-owned model favorites used by keyboard model cycling. */
2
- import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
- import { dirname } from 'node:path';
2
+ import { readJsonFile, writeJsonAtomic } from "./json-file.js";
4
3
  const MAX_FAVORITES = 16;
5
4
  function favorite(value) {
6
5
  if (typeof value !== 'object' || value === null || Array.isArray(value))
@@ -22,33 +21,24 @@ function key(value) {
22
21
  return `${value.provider}\0${value.model}`;
23
22
  }
24
23
  export function readModelFavorites(path) {
25
- try {
26
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
27
- if (!Array.isArray(parsed))
28
- return [];
29
- const seen = new Set();
30
- const result = [];
31
- for (const value of parsed) {
32
- const entry = favorite(value);
33
- if (entry === undefined || seen.has(key(entry)))
34
- continue;
35
- seen.add(key(entry));
36
- result.push(entry);
37
- if (result.length >= MAX_FAVORITES)
38
- break;
39
- }
40
- return result;
41
- }
42
- catch {
24
+ const parsed = readJsonFile(path);
25
+ if (!Array.isArray(parsed))
43
26
  return [];
27
+ const seen = new Set();
28
+ const result = [];
29
+ for (const value of parsed) {
30
+ const entry = favorite(value);
31
+ if (entry === undefined || seen.has(key(entry)))
32
+ continue;
33
+ seen.add(key(entry));
34
+ result.push(entry);
35
+ if (result.length >= MAX_FAVORITES)
36
+ break;
44
37
  }
38
+ return result;
45
39
  }
46
40
  export function writeModelFavorites(path, favorites) {
47
- const normalized = readDistinct(favorites);
48
- mkdirSync(dirname(path), { recursive: true });
49
- const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
50
- writeFileSync(temporary, JSON.stringify(normalized, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
51
- renameSync(temporary, path);
41
+ writeJsonAtomic(path, readDistinct(favorites));
52
42
  }
53
43
  export function addModelFavorite(favorites, entry) {
54
44
  return [...favorites.filter(item => key(item) !== key(entry)), entry].slice(-MAX_FAVORITES);
@@ -1 +1 @@
1
- {"version":3,"file":"model-favorites.js","sourceRoot":"","sources":["../../src/session/model-favorites.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQnC,MAAM,aAAa,GAAG,EAAE,CAAA;AAExB,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACzF,MAAM,GAAG,GAAG,KAAgC,CAAA;IAC5C,IAAI,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IACnF,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IAC7E,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC;QACzB,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;QACnB,GAAG,CAAC,OAAO,GAAG,CAAC,iBAAiB,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,iBAAiB,CAAC,KAAK,EAAE;YAC7E,CAAC,CAAC,EAAE,eAAe,EAAE,GAAG,CAAC,iBAAiB,CAAC,EAAE;YAC7C,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;AACH,CAAC;AAED,SAAS,GAAG,CAAC,KAAgD;IAC3D,OAAO,GAAG,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,KAAK,EAAE,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;QAC9D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAA;QACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;QAC9B,MAAM,MAAM,GAAoB,EAAE,CAAA;QAClC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;YAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAAE,SAAQ;YACzD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;YACpB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAClB,IAAI,MAAM,CAAC,MAAM,IAAI,aAAa;gBAAE,MAAK;QAC3C,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,SAAmC;IACnF,MAAM,UAAU,GAAG,YAAY,CAAC,SAAS,CAAC,CAAA;IAC1C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAA;IAC5D,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACvG,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAmC,EAAE,KAAoB;IACxF,OAAO,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAAA;AAC7F,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,SAAmC,EAAE,KAAgD;IACvH,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAmC,EAAE,KAAoB;IAC5F,OAAO,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;AACvE,CAAC;AAED,SAAS,YAAY,CAAC,SAAmC;IACvD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,MAAM,GAAoB,EAAE,CAAA;IAClC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAAE,SAAQ;QACzD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAAA;AACrC,CAAC"}
1
+ {"version":3,"file":"model-favorites.js","sourceRoot":"","sources":["../../src/session/model-favorites.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,6 @@
1
1
  /** Keep a Changelog parsing and startup release-note selection. */
2
+ import { compareVersions } from "./version-compare.js";
2
3
  export const STARTUP_CHANGELOG_MODES = ['summary', 'expanded', 'hidden'];
3
- function versionParts(version) {
4
- const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/u.exec(version);
5
- if (match?.[1] === undefined || match[2] === undefined || match[3] === undefined)
6
- return undefined;
7
- return [Number(match[1]), Number(match[2]), Number(match[3])];
8
- }
9
- function compareVersions(left, right) {
10
- const a = versionParts(left);
11
- const b = versionParts(right);
12
- if (a === undefined || b === undefined)
13
- return 0;
14
- for (let index = 0; index < a.length; index += 1) {
15
- const difference = (a[index] ?? 0) - (b[index] ?? 0);
16
- if (difference !== 0)
17
- return difference;
18
- }
19
- return 0;
20
- }
21
4
  function countCategories(markdown) {
22
5
  const counts = {};
23
6
  let category;
@@ -1 +1 @@
1
- {"version":3,"file":"release-notes.js","sourceRoot":"","sources":["../../src/session/release-notes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"release-notes.js","sourceRoot":"","sources":["../../src/session/release-notes.ts"],"names":[],"mappings":"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"}
@@ -15,8 +15,10 @@ import { SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
15
15
  import { queueHostSubagentPrompt } from '@deepseek-ai/dsh-subagent/internal';
16
16
  import { firstVisibleStreamTime } from "../views/stream-time.js";
17
17
  import { LiveAttemptTracker } from "./live-attempt-tracker.js";
18
+ import { blocksText } from "./content-text.js";
18
19
  import { jobNoticeFor } from "./job-notice.js";
19
- import { descendantDepth, isSteerableSubagent, SubagentRoster } from "./subagent-roster.js";
20
+ import { descendantDepth, isSteerableSubagent } from "./subagent-roster.js";
21
+ import { SubagentTracker } from "./subagent-tracker.js";
20
22
  import { commandPermission } from "../commands/permission.js";
21
23
  import { isBlankSession, toolPresentationForPreset, } from "./session-configuration.js";
22
24
  import { stripComposerImageMarkers } from "../input/image-paste.js";
@@ -245,12 +247,7 @@ function explicitSessionTitle(events) {
245
247
  function humanMessageText(event) {
246
248
  if (event.type !== 'user/message' || event.data.source.kind !== 'user')
247
249
  return undefined;
248
- const text = event.data.content
249
- .filter((block) => block.type === 'text')
250
- .map(block => block.text)
251
- .join(' ')
252
- .replace(/\s+/gu, ' ')
253
- .trim();
250
+ const text = blocksText(event.data.content, { collapse: true });
254
251
  return text === '' ? undefined : text;
255
252
  }
256
253
  /** Find direct human messages whose preceding log prefix is safe to seed into a fork. */
@@ -270,12 +267,7 @@ export function conversationTurns(events) {
270
267
  }
271
268
  if (open === undefined || event.type !== 'user/message' || event.data.source.kind !== 'user')
272
269
  continue;
273
- const text = event.data.content
274
- .filter((block) => block.type === 'text')
275
- .map(block => block.text)
276
- .join('\n')
277
- .replace(/\s+/gu, ' ')
278
- .trim();
270
+ const text = blocksText(event.data.content, { collapse: true });
279
271
  const imageCount = event.data.content.filter(block => block.type === 'image').length;
280
272
  if (text === '' && imageCount === 0)
281
273
  continue;
@@ -364,10 +356,7 @@ export function encodeCommandAttachments(images) {
364
356
  }
365
357
  /** Rehydrate one durable human inbox message into an editable composer draft. */
366
358
  export async function restoreSubmissionMessage(message, attachments) {
367
- const text = message.content
368
- .filter((block) => block.type === 'text')
369
- .map(block => block.text)
370
- .join('\n');
359
+ const text = blocksText(message.content);
371
360
  const refs = message.content
372
361
  .filter((block) => block.type === 'image')
373
362
  .map(block => block.attachment);
@@ -401,19 +390,33 @@ export class SessionRuntime {
401
390
  #releasedHandles = new WeakSet();
402
391
  #disposed = false;
403
392
  #off = [];
404
- #subagents = new SubagentRoster();
405
- #publishedSubagents;
393
+ #tracker;
406
394
  #streamAttempts = new LiveAttemptTracker();
407
395
  #recentCache = new Map();
408
396
  #refreshInFlight = null;
409
397
  #refreshDirty = false;
410
- #subagentEpoch = 0;
411
398
  #inspectEpoch = 0;
412
399
  #activationEpoch = 0;
413
400
  #inspectedId;
414
401
  constructor(ctx, tui) {
415
402
  this.#ctx = ctx;
416
403
  this.#tui = tui;
404
+ this.#tracker = new SubagentTracker({
405
+ rootId: () => this.#active?.handle.agent.id,
406
+ depth: (session) => {
407
+ const rootId = this.#active?.handle.agent.id;
408
+ return rootId === undefined ? undefined : descendantDepth(session, rootId, this.#lookupSession);
409
+ },
410
+ agentStatus: id => this.#ctx.get('agents')?.get(SessionId(id))?.status,
411
+ sessions: () => this.#ctx.get('sessions')?.list() ?? [],
412
+ listChildren: rootId => this.#ctx.get('subagents')?.listChildren(SessionId(rootId)),
413
+ publish: (roster) => {
414
+ this.#tui.setSubagents(roster);
415
+ if (this.#inspectedId === undefined)
416
+ return;
417
+ this.#tui.setInspectedSubagent(this.#inspectView(this.#inspectedId));
418
+ },
419
+ });
417
420
  this.#off.push(tui.onInspectSubagent(id => { void this.#inspectSubagent(id); }));
418
421
  this.#off.push(tui.onInspectClose(() => { this.#closeInspect(); }));
419
422
  this.#off.push(tui.onInspectSubmit(submission => { void this.#steerInspected(submission); }));
@@ -457,16 +460,13 @@ export class SessionRuntime {
457
460
  }
458
461
  if (payload.agent.id === this.#inspectedId)
459
462
  tui.setStatus(payload.status);
460
- this.#noteSubagentStatus(payload.agent.session, payload.status);
463
+ this.#tracker.noteStatus(payload.agent.session, payload.status);
461
464
  }));
462
465
  this.#off.push(ctx.on('session/created', (session) => {
463
- this.#noteSubagentSession(session);
466
+ this.#tracker.noteSession(session);
464
467
  }));
465
468
  this.#off.push(ctx.on('session/disposed', (session) => {
466
- if (!this.#subagents.owns(session.id))
467
- return;
468
- this.#subagents.setAgentStatus(session.id, 'gone');
469
- this.#pushSubagents();
469
+ this.#tracker.noteGone(session);
470
470
  }));
471
471
  this.#off.push(ctx.on('session/event', (session, event) => {
472
472
  const active = this.#active;
@@ -475,7 +475,7 @@ export class SessionRuntime {
475
475
  if (session.id === this.#inspectedId) {
476
476
  const child = ctx.get('agents')?.get(session.id);
477
477
  tui.event(event, child === undefined ? undefined : ctx.get('tuiToolPresentation')?.event(child, event));
478
- this.#noteSubagentEvent(session, event);
478
+ this.#tracker.noteEvent(session, event);
479
479
  return;
480
480
  }
481
481
  if (session === active.handle.agent.session) {
@@ -483,14 +483,14 @@ export class SessionRuntime {
483
483
  tui.event(event, ctx.get('tuiToolPresentation')?.event(active.handle.agent, event));
484
484
  }
485
485
  if (event.type === 'subagent/catalog')
486
- this.#observeCatalogChildren(session, [event]);
486
+ this.#tracker.observeCatalog(session, [event]);
487
487
  if (shouldRefreshSessionInfoAfter(event))
488
488
  this.#pushSessionInfo();
489
489
  if (event.type === 'session/title')
490
490
  void this.refreshRecent();
491
491
  return;
492
492
  }
493
- this.#noteSubagentEvent(session, event);
493
+ this.#tracker.noteEvent(session, event);
494
494
  }));
495
495
  // Format v2 moved live assistant increments off the session log. The
496
496
  // durable `session/event` firehose now carries only settlements; chunk
@@ -760,10 +760,7 @@ export class SessionRuntime {
760
760
  if (message?.type !== 'user/message' || message.data.source.kind !== 'user') {
761
761
  throw new Error('The selected conversation turn is no longer available.');
762
762
  }
763
- const text = message.data.content
764
- .filter((block) => block.type === 'text')
765
- .map(block => block.text)
766
- .join('\n');
763
+ const text = blocksText(message.data.content);
767
764
  const imageRefs = message.data.content
768
765
  .filter((block) => block.type === 'image')
769
766
  .map(block => block.attachment);
@@ -964,10 +961,9 @@ export class SessionRuntime {
964
961
  if (this.#disposed)
965
962
  return;
966
963
  this.#disposed = true;
967
- this.#subagentEpoch += 1;
968
964
  this.#activationEpoch += 1;
969
965
  this.#inspectedId = undefined;
970
- this.#subagents.reset();
966
+ this.#tracker.reset();
971
967
  this.#tui.setInspectedSubagent(undefined);
972
968
  this.#tui.setSubagents(undefined);
973
969
  this.#tui.setSessionSearch();
@@ -1009,6 +1005,28 @@ export class SessionRuntime {
1009
1005
  disposeToolPresentation: configuration.disposeToolPresentation,
1010
1006
  };
1011
1007
  }
1008
+ /**
1009
+ * Claim the activated session into the cwd workspace so grouped session
1010
+ * surfaces (the harness web sidebar) file it instead of leaving it
1011
+ * ungrouped. Best-effort like `#resolveModelInfo`: the registry is an
1012
+ * optional service and a failed claim must not disturb activation.
1013
+ */
1014
+ async #claimWorkspace(active) {
1015
+ const registry = this.#ctx.get('workspaceRegistry');
1016
+ if (registry === undefined)
1017
+ return;
1018
+ try {
1019
+ const { session } = active.handle.agent;
1020
+ const cwd = session.header.cwd;
1021
+ if (cwd === undefined)
1022
+ return;
1023
+ const workspace = await registry.resolveByPath(cwd) ?? await registry.create(cwd);
1024
+ await workspace.attachSession(session.id);
1025
+ }
1026
+ catch {
1027
+ // The workspace claim is cosmetic interop; the session itself is fine.
1028
+ }
1029
+ }
1012
1030
  async #resolveModelInfo(selection) {
1013
1031
  try {
1014
1032
  return await this.#ctx.get('llm')?.resolveModelInfo(selection.provider, selection.model);
@@ -1026,6 +1044,7 @@ export class SessionRuntime {
1026
1044
  const previous = this.#active;
1027
1045
  const epoch = ++this.#activationEpoch;
1028
1046
  this.#active = next;
1047
+ void this.#claimWorkspace(next);
1029
1048
  try {
1030
1049
  this.#presentAgent(next);
1031
1050
  const selected = this.selection(next.handle.agent);
@@ -1093,7 +1112,7 @@ export class SessionRuntime {
1093
1112
  this.#inspectedId = undefined;
1094
1113
  this.#tui.setInspectedSubagent(undefined);
1095
1114
  this.#tui.setStatus(agent.status);
1096
- this.#syncSubagents();
1115
+ this.#tracker.sync();
1097
1116
  // Seed welcome metadata before replaceSession commits the startup header to
1098
1117
  // native scrollback; later updates cannot rewrite that frozen first frame.
1099
1118
  this.#pushSessionInfo();
@@ -1163,45 +1182,6 @@ export class SessionRuntime {
1163
1182
  return list.find(skill => skill.name === name && isUserInvocable(skill));
1164
1183
  }
1165
1184
  #lookupSession = (id) => this.#ctx.get('sessions')?.get(SessionId(id));
1166
- #subagentDepth(session) {
1167
- const rootId = this.#active?.handle.agent.id;
1168
- if (rootId === undefined)
1169
- return undefined;
1170
- return descendantDepth(session, rootId, this.#lookupSession);
1171
- }
1172
- #noteSubagentSession(session) {
1173
- const depth = this.#subagentDepth(session);
1174
- if (depth === undefined)
1175
- return;
1176
- const parentId = session.header.parentSession;
1177
- this.#subagents.remember({
1178
- id: session.id,
1179
- ...(parentId === undefined ? {} : { parentId }),
1180
- depth,
1181
- phase: this.#ctx.get('agents')?.get(session.id)?.status === 'running' ? 'running' : 'starting',
1182
- });
1183
- this.#pushSubagents();
1184
- }
1185
- #noteSubagentEvent(session, event) {
1186
- const depth = this.#subagentDepth(session);
1187
- if (depth === undefined)
1188
- return;
1189
- this.#subagents.apply(session, depth, event, this.#ctx.get('agents')?.get(session.id)?.status);
1190
- this.#pushSubagents();
1191
- }
1192
- /**
1193
- * Add the direct children a Session's durable catalog declares.
1194
- *
1195
- * The live roster is built from `session/created` and child events, so a
1196
- * resumed parent would otherwise show nothing: its children are neither
1197
- * loaded nor replayed. Catalog facts are durable, so a restored roster still
1198
- * lists those children, and `#inspectSubagent` opens each transcript from
1199
- * disk. Children the live path already knows keep their own state.
1200
- */
1201
- #observeCatalogChildren(session, events) {
1202
- if (this.#subagents.observeCatalog(session.id, events))
1203
- this.#pushSubagents();
1204
- }
1205
1185
  /** Route live output only to the visible session; background frames remain buffered for inspection. */
1206
1186
  #forwardLiveDelta(agent, delta) {
1207
1187
  const active = this.#active;
@@ -1229,57 +1209,8 @@ export class SessionRuntime {
1229
1209
  this.#tui.streamDelta(delta);
1230
1210
  }
1231
1211
  }
1232
- #noteSubagentStatus(session, status) {
1233
- if (this.#subagents.owns(session.id)) {
1234
- this.#subagents.setAgentStatus(session.id, status);
1235
- this.#pushSubagents();
1236
- return;
1237
- }
1238
- const depth = this.#subagentDepth(session);
1239
- if (depth === undefined)
1240
- return;
1241
- this.#subagents.hydrate(session, depth, this.#ctx.get('agents')?.get(session.id)?.status ?? status);
1242
- this.#pushSubagents();
1243
- }
1244
- #syncSubagents() {
1245
- const root = this.#active?.handle.agent;
1246
- const epoch = this.#subagentEpoch + 1;
1247
- this.#subagentEpoch = epoch;
1248
- if (root === undefined) {
1249
- this.#subagents.reset();
1250
- this.#tui.setSubagents(undefined);
1251
- return;
1252
- }
1253
- this.#subagents.reset(root.id);
1254
- for (const session of this.#ctx.get('sessions')?.list() ?? []) {
1255
- const depth = descendantDepth(session, root.id, this.#lookupSession);
1256
- if (depth === undefined)
1257
- continue;
1258
- this.#subagents.hydrate(session, depth, this.#ctx.get('agents')?.get(session.id)?.status);
1259
- }
1260
- this.#pushSubagents();
1261
- const listed = this.#ctx.get('subagents')?.listChildren(root.id);
1262
- if (listed === undefined)
1263
- return;
1264
- void listed.then((entries) => {
1265
- if (epoch !== this.#subagentEpoch || this.#active?.handle.agent !== root)
1266
- return;
1267
- for (const entry of entries) {
1268
- if (entry.kind !== 'child')
1269
- continue;
1270
- this.#subagents.remember({
1271
- id: entry.id,
1272
- depth: 1,
1273
- mode: entry.mode,
1274
- ...(entry.label === undefined ? {} : { label: entry.label }),
1275
- phase: entry.activity === 'running' ? 'running' : 'waiting',
1276
- });
1277
- }
1278
- this.#pushSubagents();
1279
- }, () => undefined);
1280
- }
1281
1212
  #inspectView(id, fallbackPhase = 'waiting') {
1282
- const view = this.#subagents.snapshot()?.agents.find(agent => agent.id === id);
1213
+ const view = this.#tracker.snapshot()?.agents.find(agent => agent.id === id);
1283
1214
  const mode = view?.mode;
1284
1215
  return {
1285
1216
  id,
@@ -1289,28 +1220,21 @@ export class SessionRuntime {
1289
1220
  writable: isSteerableSubagent(mode),
1290
1221
  };
1291
1222
  }
1292
- #pushSubagents() {
1293
- const roster = this.#subagents.snapshot();
1294
- if (roster === this.#publishedSubagents)
1295
- return;
1296
- this.#publishedSubagents = roster;
1297
- this.#tui.setSubagents(roster);
1298
- if (this.#inspectedId === undefined)
1299
- return;
1300
- this.#tui.setInspectedSubagent(this.#inspectView(this.#inspectedId));
1301
- }
1302
1223
  #pushSessionInfo() {
1303
1224
  const active = this.#active;
1304
1225
  if (active === undefined)
1305
1226
  return;
1306
1227
  const agent = active.handle.agent;
1307
1228
  const projection = this.#projection(active);
1308
- const title = explicitSessionTitle(agent.session.snapshotEvents());
1229
+ // One snapshot feeds both folds; each append invalidates the session's
1230
+ // cached snapshot, so a second call would pay another O(log) copy.
1231
+ const events = agent.session.snapshotEvents();
1232
+ const title = explicitSessionTitle(events);
1309
1233
  this.#tui.setSession({
1310
1234
  id: agent.id,
1311
1235
  ...(title === undefined ? {} : { title }),
1312
1236
  recent: this.#recent.filter(row => row.id !== agent.id),
1313
- stats: this.#stats(active, projection),
1237
+ stats: sessionStats(events, active.contextWindow, projection),
1314
1238
  controls: this.#sessionControls(active, projection),
1315
1239
  });
1316
1240
  }
@@ -1333,7 +1257,7 @@ export class SessionRuntime {
1333
1257
  }
1334
1258
  #replaceTranscript(agent) {
1335
1259
  const events = agent.session.snapshotEvents();
1336
- this.#observeCatalogChildren(agent.session, events);
1260
+ this.#tracker.observeCatalog(agent.session, events);
1337
1261
  this.#tui.replaceSession(events, this.#ctx.get('tuiToolPresentation')?.session(agent, events), agent.status);
1338
1262
  this.#replayLivePrefix(agent.session.id);
1339
1263
  }
@@ -1361,7 +1285,7 @@ export class SessionRuntime {
1361
1285
  async #inspectSubagent(id) {
1362
1286
  const request = this.#inspectEpoch + 1;
1363
1287
  this.#inspectEpoch = request;
1364
- if (!this.#subagents.owns(id)) {
1288
+ if (!this.#tracker.owns(id)) {
1365
1289
  this.#tui.notice('That subagent is no longer available.', { level: 'error' });
1366
1290
  return;
1367
1291
  }
@@ -1408,7 +1332,7 @@ export class SessionRuntime {
1408
1332
  return;
1409
1333
  }
1410
1334
  const live = this.#ctx.get('sessions')?.get(SessionId(childId));
1411
- const rosterParent = this.#subagents.snapshot()?.agents.find(agent => agent.id === childId)?.parentId;
1335
+ const rosterParent = this.#tracker.snapshot()?.agents.find(agent => agent.id === childId)?.parentId;
1412
1336
  const parentId = live?.header.parentSession ?? rosterParent;
1413
1337
  const parent = parentId === undefined || parentId === root.id
1414
1338
  ? root