@itookit/dsht 0.5.1 → 0.6.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 (93) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +10 -4
  3. package/README.zh.md +12 -6
  4. package/dist/catalog/controller.d.ts +26 -6
  5. package/dist/catalog/controller.js +73 -45
  6. package/dist/catalog/index.d.ts +1 -0
  7. package/dist/cli/dsht.js +22 -2
  8. package/dist/cli/startup.js +30 -11
  9. package/dist/cli/verifier.d.ts +4 -0
  10. package/dist/cli/verifier.js +28 -5
  11. package/dist/contracts.d.ts +42 -5
  12. package/dist/controller/connection-streams.d.ts +22 -0
  13. package/dist/controller/connection-streams.js +105 -0
  14. package/dist/controller/connection.d.ts +14 -3
  15. package/dist/controller/connection.js +40 -69
  16. package/dist/controller/controller.d.ts +20 -234
  17. package/dist/controller/controller.js +113 -811
  18. package/dist/controller/foreground.d.ts +44 -0
  19. package/dist/controller/foreground.js +79 -0
  20. package/dist/controller/loop-coordinator.d.ts +48 -0
  21. package/dist/controller/loop-coordinator.js +647 -0
  22. package/dist/controller/loop-prompts-schema.d.ts +16 -2
  23. package/dist/controller/loop-prompts-schema.js +106 -27
  24. package/dist/controller/loop-prompts.d.ts +17 -2
  25. package/dist/controller/loop-prompts.generated.js +2 -1
  26. package/dist/controller/loop-prompts.js +35 -9
  27. package/dist/controller/loop-protocols.d.ts +3 -1
  28. package/dist/controller/loop-protocols.js +8 -3
  29. package/dist/controller/loop-source.d.ts +74 -0
  30. package/dist/controller/loop-source.js +224 -0
  31. package/dist/controller/verifier.d.ts +4 -0
  32. package/dist/cost/controller.d.ts +3 -1
  33. package/dist/cost/controller.js +26 -7
  34. package/dist/cost/index.d.ts +1 -1
  35. package/dist/cost/index.js +1 -1
  36. package/dist/cost/ledger-files.d.ts +20 -0
  37. package/dist/cost/ledger-files.js +115 -15
  38. package/dist/cost/ledger.d.ts +31 -6
  39. package/dist/cost/ledger.js +74 -22
  40. package/dist/cost/pricing.d.ts +39 -0
  41. package/dist/cost/pricing.js +46 -0
  42. package/dist/cost/scanner.js +1 -0
  43. package/dist/cost/types.d.ts +9 -3
  44. package/dist/session/controller.d.ts +23 -35
  45. package/dist/session/controller.js +113 -363
  46. package/dist/session/history-reader.d.ts +32 -0
  47. package/dist/session/history-reader.js +170 -0
  48. package/dist/session/index.d.ts +1 -1
  49. package/dist/session/info.d.ts +3 -38
  50. package/dist/session/info.js +14 -1
  51. package/dist/session/interactions.d.ts +26 -0
  52. package/dist/session/interactions.js +75 -0
  53. package/dist/session/navigator.d.ts +47 -0
  54. package/dist/session/navigator.js +158 -0
  55. package/dist/session/prompt-backfill.d.ts +23 -0
  56. package/dist/session/prompt-backfill.js +88 -0
  57. package/dist/session/state.d.ts +20 -0
  58. package/dist/session/state.js +1 -0
  59. package/dist/session/telemetry.d.ts +15 -6
  60. package/dist/session/telemetry.js +44 -7
  61. package/dist/session/transcript.d.ts +5 -1
  62. package/dist/slash/index.d.ts +1 -1
  63. package/dist/slash/parse.d.ts +2 -126
  64. package/dist/slash/registry.d.ts +1 -1
  65. package/dist/slash/types.d.ts +126 -0
  66. package/dist/slash/types.js +1 -0
  67. package/dist/state.d.ts +5 -17
  68. package/dist/state.js +1 -1
  69. package/dist/storage/files.d.ts +8 -0
  70. package/dist/storage/files.js +18 -1
  71. package/dist/storage/index.d.ts +1 -1
  72. package/dist/storage/index.js +1 -1
  73. package/dist/transport/client.d.ts +4 -3
  74. package/dist/transport/client.js +71 -25
  75. package/dist/ui/app.js +88 -301
  76. package/dist/ui/chat/shell-view.d.ts +2 -0
  77. package/dist/ui/chat/shell-view.js +8 -0
  78. package/dist/ui/chat/use-history-view.d.ts +69 -0
  79. package/dist/ui/chat/use-history-view.js +123 -0
  80. package/dist/ui/dialogs/cost.d.ts +6 -0
  81. package/dist/ui/dialogs/cost.js +5 -1
  82. package/dist/ui/dialogs/loop.d.ts +5 -4
  83. package/dist/ui/dialogs/loop.js +14 -6
  84. package/dist/ui/dialogs/use-panels.d.ts +53 -0
  85. package/dist/ui/dialogs/use-panels.js +51 -0
  86. package/dist/ui/input/use-composer.d.ts +35 -0
  87. package/dist/ui/input/use-composer.js +109 -0
  88. package/dist/ui/input/use-deferred-lines.d.ts +16 -0
  89. package/dist/ui/input/use-deferred-lines.js +54 -0
  90. package/dist/ui/input/use-history-recall.d.ts +20 -0
  91. package/dist/ui/input/use-history-recall.js +47 -0
  92. package/loop.yaml +230 -0
  93. package/package.json +5 -4
@@ -1,9 +1,9 @@
1
- import { chargeFor, costDay, DEFAULT_PRICES, pricesDigest, PRICING_ENGINE_VERSION } from "./pricing.js";
1
+ import { chargeFor, costDay, costMonthStart, costWeekStart, costWindowStart, DEFAULT_PRICES, pricesDigest, PRICING_ENGINE_VERSION } from "./pricing.js";
2
2
  import { foldSamples } from "./records.js";
3
- import { loadLedgers, saveLedger } from "./ledger-files.js";
3
+ import { loadLedgers, pruneLedgers, saveLedger } from "./ledger-files.js";
4
4
  /** Reasons one slice keeps at most, so a broken table cannot grow the ledger without bound. */
5
5
  const UNPRICED_LIMIT = 8;
6
- /** Per-origin cache of folded session totals; every scan replaces a session at its cut. */
6
+ /** Per-origin cache of folded session totals and day buckets; every scan replaces a session at its cut. */
7
7
  export class CostLedger {
8
8
  prices;
9
9
  directory;
@@ -34,10 +34,19 @@ export class CostLedger {
34
34
  return 'partial';
35
35
  return this.scannedAt !== undefined || this.sessions.size > 0 ? 'complete' : 'partial';
36
36
  }
37
- /** Load the newest cut per session; a stored total is read as it was decided. */
38
- async load() {
37
+ /** Load the newest cut per session; a stored total is read as it was decided.
38
+ *
39
+ * Slices and dead files that have left the retention window are deleted in the same pass, so the
40
+ * directory cannot grow with every session this client has ever seen. A slice is a projection of the
41
+ * host log, so letting one go costs a rescan of that session, never data.
42
+ * @param now - Clock that names the window, so a caller or a test can pin the boundary.
43
+ */
44
+ async load(now = Date.now()) {
39
45
  this.totals.clear();
40
46
  this.sessions = await loadLedgers(this.directory);
47
+ for (const sessionId of await pruneLedgers(this.directory, costWindowStart(now), this.sessions)) {
48
+ this.sessions.delete(sessionId);
49
+ }
41
50
  }
42
51
  /** Replace one session using all billing events through the opening snapshot cut.
43
52
  *
@@ -54,16 +63,28 @@ export class CostLedger {
54
63
  const current = this.sessions.get(sessionId);
55
64
  if ((current?.cut ?? -2) > cut)
56
65
  return;
57
- const day = costDay(now);
66
+ const scanDay = costDay(now);
67
+ const floor = costWindowStart(now);
58
68
  const total = { amount: 0, unknown: 0, records: 0 };
59
- const today = { day, amount: 0, unknown: 0, records: 0 };
69
+ const days = new Map();
60
70
  const unpriced = new Set();
71
+ /** The day bucket to add to, created on first use so a session reports only the days it touched. */
72
+ const dayBucket = (day) => {
73
+ const found = days.get(day);
74
+ if (found !== undefined)
75
+ return found;
76
+ const created = { day, amount: 0, unknown: 0, records: 0 };
77
+ days.set(day, created);
78
+ return created;
79
+ };
61
80
  for (const sample of foldSamples(events)) {
62
81
  const decision = chargeFor(this.prices, sample.provider, sample.model, sample.time, sample.usage);
63
- // A request belongs to the day it settled on, so the day bucket only counts the requests of
64
- // the calendar day this scan is running on; another day reads as nothing spent today. A request
65
- // with no settlement time belongs to no day, so it is counted as unknown wherever it is read.
66
- const buckets = sample.time === undefined || costDay(sample.time) === day ? [total, today] : [total];
82
+ // A request belongs to the day it settled on. One with no settlement time is attributed to the
83
+ // day of this scan, which is the only day it can reach without guessing a tariff band, and is
84
+ // where a reader looks for what makes today's total inexact.
85
+ const day = sample.time === undefined ? scanDay : costDay(sample.time);
86
+ // Only the window is stored, so a session a year old writes days, not a year of them.
87
+ const buckets = day < floor ? [total] : [total, dayBucket(day)];
67
88
  for (const bucket of buckets) {
68
89
  bucket.records++;
69
90
  if (decision.amount === undefined)
@@ -74,8 +95,8 @@ export class CostLedger {
74
95
  if (decision.amount === undefined && unpriced.size < UNPRICED_LIMIT)
75
96
  unpriced.add(`${sample.provider}/${sample.model}: ${decision.reason}`);
76
97
  }
77
- const saved = { version: 3, sessionId, cut, engine: PRICING_ENGINE_VERSION, catalog: this.catalog,
78
- total, day: today, unpriced: [...unpriced] };
98
+ const saved = { version: 4, sessionId, cut, engine: PRICING_ENGINE_VERSION, catalog: this.catalog,
99
+ total, days: [...days.values()].sort((a, b) => a.day < b.day ? -1 : a.day > b.day ? 1 : 0), unpriced: [...unpriced] };
79
100
  // Another process may have persisted a newer cut of this session since it was last read.
80
101
  if (this.directory && !await saveLedger(this.directory, saved))
81
102
  return;
@@ -117,25 +138,56 @@ export class CostLedger {
117
138
  }
118
139
  /** Every session's requests on one Beijing calendar day.
119
140
  *
120
- * A slice keeps only the day its last scan ran on, so another day reads as nothing spent today
121
- * rather than as the last day that was scanned.
141
+ * The day is a real bucket rather than only the day a scan ran on, so asking for another day
142
+ * reports that day's spend; the retention window is what limits how far back the answer reaches.
122
143
  * @param now - Clock that names the day to report.
123
144
  * @returns The day's total across cached sessions.
124
145
  */
125
146
  today(now = Date.now()) {
126
147
  const day = costDay(now);
127
- const cached = this.totals.get(`d:${day}`);
148
+ return this.period(day, day);
149
+ }
150
+ /** Every session's requests in the natural week containing a day, through that day.
151
+ * @param now - Clock that names the day to report.
152
+ * @returns The week-to-date total across cached sessions.
153
+ */
154
+ week(now = Date.now()) {
155
+ const day = costDay(now);
156
+ return this.period(costWeekStart(day), day);
157
+ }
158
+ /** Every session's requests in the natural month containing a day, through that day.
159
+ * @param now - Clock that names the day to report.
160
+ * @returns The month-to-date total across cached sessions.
161
+ */
162
+ month(now = Date.now()) {
163
+ const day = costDay(now);
164
+ return this.period(costMonthStart(day), day);
165
+ }
166
+ /** Sum stored day buckets over a Beijing day range, inclusive at both ends.
167
+ *
168
+ * Days outside the retention window are absent rather than zero, so a range wider than the window
169
+ * reports what is still kept — which is why the fold and this sum share one window constant.
170
+ * @param from - First day to include, YYYY-MM-DD.
171
+ * @param to - Last day to include, YYYY-MM-DD.
172
+ * @returns The range's total across cached sessions.
173
+ */
174
+ period(from, to) {
175
+ const key = `p:${from}..${to}`;
176
+ const cached = this.totals.get(key);
128
177
  if (cached)
129
178
  return cached;
130
179
  const result = { amount: 0, unknown: 0, records: 0 };
131
180
  for (const session of this.sessions.values()) {
132
- if (session.day.day !== day)
133
- continue;
134
- result.amount += session.day.amount;
135
- result.unknown += session.day.unknown;
136
- result.records += session.day.records;
181
+ for (const day of session.days) {
182
+ // ISO day strings compare chronologically, so a lexical range is the day range.
183
+ if (day.day < from || day.day > to)
184
+ continue;
185
+ result.amount += day.amount;
186
+ result.unknown += day.unknown;
187
+ result.records += day.records;
188
+ }
137
189
  }
138
- this.totals.set(`d:${day}`, result);
190
+ this.totals.set(key, result);
139
191
  return result;
140
192
  }
141
193
  }
@@ -29,6 +29,45 @@ export declare function pricesFrom(value: unknown): PriceVersion[];
29
29
  * @returns Beijing calendar date, YYYY-MM-DD.
30
30
  */
31
31
  export declare function costDay(time: number): string;
32
+ /** Beijing days of cost the ledger keeps, folds and reports.
33
+ *
34
+ * One number decides three things that have to agree: how much per-session history a slice stores,
35
+ * which slices a retention pass may delete, and how far back the day/week/month totals reach. Sixty
36
+ * days covers a natural month with room to compare it against the previous one.
37
+ */
38
+ export declare const COST_WINDOW_DAYS = 60;
39
+ /** First millisecond of a Beijing calendar day.
40
+ *
41
+ * China has had no daylight saving since 1991, so a Beijing day is exactly 24 hours and an explicit
42
+ * offset is exact rather than an approximation.
43
+ * @param day - Beijing calendar date, YYYY-MM-DD.
44
+ * @returns Epoch milliseconds.
45
+ */
46
+ export declare function costDayStart(day: string): number;
47
+ /** The Beijing day a whole number of days before another.
48
+ *
49
+ * The arithmetic is on the calendar date, not on an instant, so it cannot drift across a month, a
50
+ * year or a leap day.
51
+ * @param day - Beijing calendar date, YYYY-MM-DD.
52
+ * @param days - Days to subtract, zero or more.
53
+ * @returns Beijing calendar date, YYYY-MM-DD.
54
+ */
55
+ export declare function costDaysBefore(day: string, days: number): string;
56
+ /** The Monday that begins the natural week containing a Beijing day.
57
+ * @param day - Beijing calendar date, YYYY-MM-DD.
58
+ * @returns Beijing calendar date of that week's Monday.
59
+ */
60
+ export declare function costWeekStart(day: string): string;
61
+ /** The first day of the natural month containing a Beijing day.
62
+ * @param day - Beijing calendar date, YYYY-MM-DD.
63
+ * @returns Beijing calendar date, YYYY-MM-01.
64
+ */
65
+ export declare function costMonthStart(day: string): string;
66
+ /** Oldest Beijing day a ledger still keeps, so every retention decision uses one boundary.
67
+ * @param now - Clock that names the current day.
68
+ * @returns Beijing calendar date, YYYY-MM-DD.
69
+ */
70
+ export declare function costWindowStart(now: number): string;
32
71
  /** Canonical form of a model name before matching.
33
72
  *
34
73
  * The host reports names that differ from the published table by width, case, surrounding space, or
@@ -117,6 +117,52 @@ export function pricesFrom(value) {
117
117
  * @returns Beijing calendar date, YYYY-MM-DD.
118
118
  */
119
119
  export function costDay(time) { return new Date(time + 8 * 3600_000).toISOString().slice(0, 10); }
120
+ /** Beijing days of cost the ledger keeps, folds and reports.
121
+ *
122
+ * One number decides three things that have to agree: how much per-session history a slice stores,
123
+ * which slices a retention pass may delete, and how far back the day/week/month totals reach. Sixty
124
+ * days covers a natural month with room to compare it against the previous one.
125
+ */
126
+ export const COST_WINDOW_DAYS = 60;
127
+ /** First millisecond of a Beijing calendar day.
128
+ *
129
+ * China has had no daylight saving since 1991, so a Beijing day is exactly 24 hours and an explicit
130
+ * offset is exact rather than an approximation.
131
+ * @param day - Beijing calendar date, YYYY-MM-DD.
132
+ * @returns Epoch milliseconds.
133
+ */
134
+ export function costDayStart(day) { return Date.parse(`${day}T00:00:00+08:00`); }
135
+ /** The Beijing day a whole number of days before another.
136
+ *
137
+ * The arithmetic is on the calendar date, not on an instant, so it cannot drift across a month, a
138
+ * year or a leap day.
139
+ * @param day - Beijing calendar date, YYYY-MM-DD.
140
+ * @param days - Days to subtract, zero or more.
141
+ * @returns Beijing calendar date, YYYY-MM-DD.
142
+ */
143
+ export function costDaysBefore(day, days) {
144
+ const [year, month, date] = day.split('-').map(Number);
145
+ return new Date(Date.UTC(year, month - 1, date - days)).toISOString().slice(0, 10);
146
+ }
147
+ /** The Monday that begins the natural week containing a Beijing day.
148
+ * @param day - Beijing calendar date, YYYY-MM-DD.
149
+ * @returns Beijing calendar date of that week's Monday.
150
+ */
151
+ export function costWeekStart(day) {
152
+ const [year, month, date] = day.split('-').map(Number);
153
+ const weekday = new Date(Date.UTC(year, month - 1, date)).getUTCDay();
154
+ return costDaysBefore(day, (weekday + 6) % 7);
155
+ }
156
+ /** The first day of the natural month containing a Beijing day.
157
+ * @param day - Beijing calendar date, YYYY-MM-DD.
158
+ * @returns Beijing calendar date, YYYY-MM-01.
159
+ */
160
+ export function costMonthStart(day) { return `${day.slice(0, 8)}01`; }
161
+ /** Oldest Beijing day a ledger still keeps, so every retention decision uses one boundary.
162
+ * @param now - Clock that names the current day.
163
+ * @returns Beijing calendar date, YYYY-MM-DD.
164
+ */
165
+ export function costWindowStart(now) { return costDaysBefore(costDay(now), COST_WINDOW_DAYS - 1); }
120
166
  /** Canonical form of a model name before matching.
121
167
  *
122
168
  * The host reports names that differ from the published table by width, case, surrounding space, or
@@ -45,6 +45,7 @@ export async function sessionCostHistory(client, session, signal, onPage, onReco
45
45
  }
46
46
  /** Page one addressed session's history into the billing events the ledger folds. */
47
47
  async function readCostHistory(client, address, signal, onPage, onRecords) {
48
+ signal.throwIfAborted();
48
49
  onPage?.();
49
50
  const snapshot = await new Promise((resolve, reject) => {
50
51
  let sub;
@@ -52,7 +52,7 @@ export interface CostTotal {
52
52
  unknown: number;
53
53
  records: number;
54
54
  }
55
- /** One Beijing calendar day's requests, kept only for the day a scan ran on. */
55
+ /** One Beijing calendar day's requests, kept for every day inside the retention window. */
56
56
  export interface DayTotal extends CostTotal {
57
57
  day: string;
58
58
  }
@@ -64,14 +64,20 @@ export interface DayTotal extends CostTotal {
64
64
  * rules and the table behind these totals, so a process holding an older one cannot overwrite them.
65
65
  */
66
66
  export interface SavedCost {
67
- version: 3;
67
+ version: 4;
68
68
  sessionId: string;
69
69
  /** Durable sequence the fold reached; a scan that opened an older cut may not replace this slice. */
70
70
  cut: number;
71
71
  engine: number;
72
72
  catalog: string;
73
73
  total: CostTotal;
74
- day: DayTotal;
74
+ /** One entry per Beijing day this session spent inside the retention window, oldest first.
75
+ *
76
+ * Days older than the window are dropped as the slice is written, so the file cannot grow with a
77
+ * session's history. A day a later scan no longer reports is simply gone from the next slice: the
78
+ * host log, not this file, is what a day's total is re-derived from.
79
+ */
80
+ days: DayTotal[];
75
81
  /** Distinct reasons an amount is missing, bounded, so a panel can say what makes a total inexact. */
76
82
  unpriced: string[];
77
83
  }
@@ -1,6 +1,6 @@
1
1
  import type { HostAccess } from '../transport/host.ts';
2
2
  import { type Json, type ObjectValue } from '../transport/wire.ts';
3
- import type { ControllerStore, State } from '../state.ts';
3
+ import type { SessionStore } from './state.ts';
4
4
  import { type SessionRender } from './history.ts';
5
5
  import type { HistoryLimits } from './memory.ts';
6
6
  import { type FileReference } from './references.ts';
@@ -19,16 +19,18 @@ export declare class SessionController {
19
19
  private readonly connection;
20
20
  private readonly historyLimits;
21
21
  private follow;
22
- private interactions;
22
+ private readonly history;
23
+ private readonly interactions;
24
+ private readonly navigation;
23
25
  /** Reading protection: reclamation pauses while the reader is away from the live end. */
24
26
  private historyPinned;
25
27
  /** Host runtime mirrors for every session this connection has seen. */
26
28
  private readonly runtime;
27
29
  /** Cancels the background prompt backfill of the previous selection. */
28
- private promptBackfill?;
30
+ private readonly promptBackfill;
29
31
  /** Prompts of sessions this process has already read, so re-opening one costs no page request. */
30
32
  private readonly promptCache;
31
- /** Prompt index, composer and reading view of the selected session; the instance `State.session` exposes. */
33
+ /** Prompt index, record, history window and interaction state of the selected session. */
32
34
  private get info();
33
35
  private get prompts();
34
36
  private stoppingSession?;
@@ -36,13 +38,13 @@ export declare class SessionController {
36
38
  private admission;
37
39
  /** Admission order of this session's writes, so two concurrent decisions cannot interleave. */
38
40
  private readonly mutations;
39
- constructor(store: ControllerStore, host: HostAccess, connection: ConnectionView, historyLimits: HistoryLimits, report?: (admission: MutationAdmission) => void);
41
+ constructor(store: SessionStore, host: HostAccess, connection: ConnectionView, historyLimits: HistoryLimits, report?: (admission: MutationAdmission) => void);
40
42
  /** Host running state covers model generation, tools, and waits between assistant attempts. */
41
43
  get running(): boolean;
42
44
  /** Current title projection, falling back to the list title and then the session ID. */
43
45
  get sessionName(): string | undefined;
44
46
  /** Current agent-preset name, matching the web header's built-in labels and custom metadata. */
45
- get sessionMode(): string | undefined;
47
+ sessionMode(presets?: readonly ObjectValue[]): string | undefined;
46
48
  /** Epoch start from the retained turn log, or when this client first observed the run. */
47
49
  get workingSince(): number | undefined;
48
50
  /** Present only sessions explicitly accounted to the selected workspace. */
@@ -76,15 +78,15 @@ export declare class SessionController {
76
78
  beginGeneration(): void;
77
79
  /** Invalidate in-flight work and drop transient interactions when a generation ends. */
78
80
  endGeneration(): void;
79
- /** Wait for an in-flight cancellation so shutdown leaves nothing running. */
81
+ /** Wait for cancellation and foreground history tasks before releasing their records. */
80
82
  settle(): Promise<void>;
81
83
  /** Release the selected transcript and its layout caches. */
82
84
  release(): void;
83
85
  /** Pending interactions for the selected chat session, derived independently of frame order.
84
- * @param state - State being published.
86
+ * @param sessionId - Session whose pending interactions are being published.
85
87
  * @returns Retained question and approval frames for that session.
86
88
  */
87
- pendingFor(state: State): PendingInteraction[];
89
+ pendingFor(sessionId: string | undefined): PendingInteraction[];
88
90
  /** Unanswered interactions by session, so a list can show who is waiting without opening them.
89
91
  *
90
92
  * The host delivers approval and question waterfalls for every session on one stream, and this
@@ -127,7 +129,9 @@ export declare class SessionController {
127
129
  /** Refresh both lists from the host, then show the requested picker.
128
130
  * @param screen - Picker to display after the refresh.
129
131
  */
130
- showPicker(screen: 'workspaces' | 'sessions'): Promise<void>;
132
+ showPicker(screen: 'workspaces' | 'sessions', signal?: AbortSignal): Promise<void>;
133
+ /** Refresh navigation data without taking the reader to another screen. */
134
+ refreshLists(): Promise<void>;
131
135
  /** Return to the selected conversation without re-selecting it.
132
136
  *
133
137
  * A picker opened over a conversation is a detour: leaving it must not re-subscribe, reload the
@@ -152,11 +156,11 @@ export declare class SessionController {
152
156
  /** Open a workspace picker, or resolve a workspace by ID, exact title/path, or unique ID prefix.
153
157
  * @param query - Workspace target, if any.
154
158
  */
155
- switchWorkspace(query?: string): Promise<void>;
159
+ switchWorkspace(query?: string, signal?: AbortSignal): Promise<void>;
156
160
  /** Guide workspace selection, list all sessions with `all`, or resolve an exact session target.
157
161
  * @param query - Session target, `all`, or nothing for the guided picker.
158
162
  */
159
- switchSession(query?: string): Promise<void>;
163
+ switchSession(query?: string, signal?: AbortSignal): Promise<void>;
160
164
  /** Prompt for a host path without starting a local agent. */
161
165
  enterPath(): void;
162
166
  /** Adopt the workspace whose registered path contains the directory this client runs in.
@@ -171,9 +175,9 @@ export declare class SessionController {
171
175
  /** Register a host directory and move to its session picker.
172
176
  * @param path - Absolute directory path on the host.
173
177
  */
174
- createWorkspace(path: string): Promise<void>;
178
+ createWorkspace(path: string, signal?: AbortSignal): Promise<void>;
175
179
  /** Create a session only after the user explicitly selects New session. */
176
- createSession(): Promise<void>;
180
+ createSession(signal?: AbortSignal): Promise<void>;
177
181
  /** Create a session for another purpose without selecting it, named so a reader can tell it apart.
178
182
  *
179
183
  * A verifier runs in its own session while the reviewed session stays selected, so this never
@@ -193,6 +197,9 @@ export declare class SessionController {
193
197
  * @param sessionId - Session to follow.
194
198
  */
195
199
  selectSession(sessionId: string): Promise<void>;
200
+ /** Reattach the selected conversation after reconnect, keeping the current navigation context. */
201
+ restoreSelectedSession(): void;
202
+ private followSession;
196
203
  /** Wait for the selected follow snapshot, failing on disconnect or cancellation.
197
204
  * @param signal - Cancels waiting without closing the session.
198
205
  */
@@ -311,11 +318,6 @@ export declare class SessionController {
311
318
  * @returns Whether any older prompt was recovered.
312
319
  */
313
320
  refillRecall(): boolean;
314
- /** Seed recall from a complete cached entry, so an open that follows a scan costs no request.
315
- * @param sessionId - Session being opened.
316
- * @returns Whether the cache covered this session.
317
- */
318
- private adoptCachedPrompts;
319
321
  /** Fold one history page the cost scan already read into the prompt cache.
320
322
  *
321
323
  * The scan reads every session's whole history on connect, so this is where two readers stop
@@ -328,29 +330,17 @@ export declare class SessionController {
328
330
  * @param sessionId - Session the scan finished.
329
331
  */
330
332
  rememberScanDone(sessionId: string): void;
331
- /** Fold every prompt the host still holds into the recall index, in the background.
332
- *
333
- * Session start delivers only the newest window, so without this the arrows could reach older
334
- * prompts but not show them without paging first. Each page is parsed into a temporary transcript
335
- * and only its prompts are kept, so the live record, its memory window and the row cache never
336
- * grow. The walk is bounded and the next selection cancels it; anything past the bound is still
337
- * reachable through the lazy backward step.
338
- * @param sessionId - Session being opened.
339
- * @param selection - Selector generation that must still be current.
340
- */
341
- private backfillPrompts;
342
333
  /** Search one page at a time, preserving only the first 200 matches and releasing temporary content.
343
334
  * @param query - Literal, case-insensitive text including folded reasoning.
344
335
  * @param signal - Cancels HTTP and processing without cancelling the agent.
345
336
  * @returns Newest-first bounded summaries and an explicit truncation flag.
346
337
  */
347
338
  searchHistory(query: string, signal: AbortSignal): Promise<HistorySearch>;
348
- /** Load a separate small window ending at a search target; the live transcript keeps following.
339
+ /** Select a readable window by sequence; temporary records never escape to the UI.
349
340
  * @param target - Durable message sequence to display.
350
341
  * @param signal - Cancels the target-page request.
351
- * @returns A caller-owned historical window that must be disposed when closed.
352
342
  */
353
- historyAt(target: number, signal: AbortSignal): Promise<Transcript>;
343
+ openHistory(target: number, signal: AbortSignal): Promise<void>;
354
344
  /** Load the prefix required for an explicit history jump; never loop on an unadvancing page.
355
345
  * @param target - Visible record sequence, or first for the oldest available history.
356
346
  * @param signal - Cancels local paging without interrupting the remote agent.
@@ -364,6 +354,4 @@ export declare class SessionController {
364
354
  private releaseTranscript;
365
355
  /** @returns The selected session identity, or a `Select a session first` failure. */
366
356
  private get sessionId();
367
- /** Answer one retained waterfall through the connection's event-result endpoint. */
368
- private reply;
369
357
  }