@aiwayds/dsh-tui-pi 0.1.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +324 -0
  3. package/bin/dsh-tui-pi +5 -0
  4. package/cordis.patch.yml +9 -0
  5. package/lib/append-system.d.ts +66 -0
  6. package/lib/append-system.js +161 -0
  7. package/lib/append-system.js.map +1 -0
  8. package/lib/commands.d.ts +53 -0
  9. package/lib/commands.js +167 -0
  10. package/lib/commands.js.map +1 -0
  11. package/lib/dsh-events.d.ts +106 -0
  12. package/lib/dsh-events.js +30 -0
  13. package/lib/dsh-events.js.map +1 -0
  14. package/lib/editor.d.ts +28 -0
  15. package/lib/editor.js +70 -0
  16. package/lib/editor.js.map +1 -0
  17. package/lib/footer.d.ts +36 -0
  18. package/lib/footer.js +112 -0
  19. package/lib/footer.js.map +1 -0
  20. package/lib/frame.d.ts +35 -0
  21. package/lib/frame.js +75 -0
  22. package/lib/frame.js.map +1 -0
  23. package/lib/git.d.ts +17 -0
  24. package/lib/git.js +51 -0
  25. package/lib/git.js.map +1 -0
  26. package/lib/index.d.ts +15 -0
  27. package/lib/index.js +781 -0
  28. package/lib/index.js.map +1 -0
  29. package/lib/instructions.d.ts +29 -0
  30. package/lib/instructions.js +67 -0
  31. package/lib/instructions.js.map +1 -0
  32. package/lib/live-widgets.d.ts +85 -0
  33. package/lib/live-widgets.js +218 -0
  34. package/lib/live-widgets.js.map +1 -0
  35. package/lib/messages.d.ts +277 -0
  36. package/lib/messages.js +734 -0
  37. package/lib/messages.js.map +1 -0
  38. package/lib/permission.d.ts +27 -0
  39. package/lib/permission.js +48 -0
  40. package/lib/permission.js.map +1 -0
  41. package/lib/provider-catalog.d.ts +114 -0
  42. package/lib/provider-catalog.js +124 -0
  43. package/lib/provider-catalog.js.map +1 -0
  44. package/lib/quotes.d.ts +28 -0
  45. package/lib/quotes.js +144 -0
  46. package/lib/quotes.js.map +1 -0
  47. package/lib/reload.d.ts +23 -0
  48. package/lib/reload.js +171 -0
  49. package/lib/reload.js.map +1 -0
  50. package/lib/selectors.d.ts +48 -0
  51. package/lib/selectors.js +261 -0
  52. package/lib/selectors.js.map +1 -0
  53. package/lib/session.d.ts +157 -0
  54. package/lib/session.js +555 -0
  55. package/lib/session.js.map +1 -0
  56. package/lib/sessions.d.ts +73 -0
  57. package/lib/sessions.js +253 -0
  58. package/lib/sessions.js.map +1 -0
  59. package/lib/settings.d.ts +180 -0
  60. package/lib/settings.js +1328 -0
  61. package/lib/settings.js.map +1 -0
  62. package/lib/text.d.ts +22 -0
  63. package/lib/text.js +45 -0
  64. package/lib/text.js.map +1 -0
  65. package/lib/theme/index.d.ts +79 -0
  66. package/lib/theme/index.js +121 -0
  67. package/lib/theme/index.js.map +1 -0
  68. package/lib/theme/palette.d.ts +56 -0
  69. package/lib/theme/palette.js +154 -0
  70. package/lib/theme/palette.js.map +1 -0
  71. package/lib/theme-settings.d.ts +68 -0
  72. package/lib/theme-settings.js +223 -0
  73. package/lib/theme-settings.js.map +1 -0
  74. package/lib/tui.d.ts +70 -0
  75. package/lib/tui.js +206 -0
  76. package/lib/tui.js.map +1 -0
  77. package/lib/welcome.d.ts +91 -0
  78. package/lib/welcome.js +281 -0
  79. package/lib/welcome.js.map +1 -0
  80. package/package.json +50 -0
  81. package/patches/@earendil-works__pi-tui.patch +72 -0
  82. package/pnpm-workspace.yaml +2 -0
  83. package/templates/APPEND_SYSTEM.md +34 -0
package/lib/session.js ADDED
@@ -0,0 +1,555 @@
1
+ /**
2
+ * Session bridge between the dsh tree and the TUI.
3
+ *
4
+ * Responsibilities:
5
+ * - lazy agent creation on first prompt (openma runner pattern)
6
+ * - prompt delivery via agent.followup()
7
+ * - session-event subscription filtered to the bridge's session
8
+ * - O(1) incremental stats maintained as events arrive (footer reads these —
9
+ * never re-scans the session log, per the pi-turbo findings)
10
+ * - resume of persisted sessions (log replay into stats + transcript)
11
+ */
12
+ import { installModelSelection } from '@deepseek-ai/dsh-agent';
13
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
14
+ import { settingsNamespace, SettingsConflictError } from '@deepseek-ai/dsh-settings';
15
+ import { SessionId } from '@deepseek-ai/dsh-session';
16
+ import { isAgentEnd, isAgentStart, isLlmRetry, isSubagentDescriptor } from "./dsh-events.js";
17
+ export class DshSessionBridge {
18
+ ctx;
19
+ callbacks;
20
+ handle;
21
+ creating;
22
+ resuming;
23
+ disposers = [];
24
+ sessionId;
25
+ selection;
26
+ /** Mutable live selection installed into the agent; `/model` mutates `current`. */
27
+ selectionRef = { current: undefined, assembled: undefined };
28
+ /** Mirror of the agent's `agent/status`; `isRunning()` reads this. */
29
+ running = false;
30
+ stats = {
31
+ inputTokens: 0,
32
+ outputTokens: 0,
33
+ cacheReadTokens: 0,
34
+ cacheWriteTokens: 0,
35
+ msgCount: 0,
36
+ toolCallCount: 0,
37
+ };
38
+ /** Live subagent rows keyed by child session id. */
39
+ agentViews = new Map();
40
+ /** Child session ids whose own events we fold into the agent views. */
41
+ childSessions = new Set();
42
+ /**
43
+ * Session ids that can own tracked children (this session first, then every
44
+ * tracked child — delegation nests). Used to match `parentSession` headers
45
+ * and to fold workflow events of the parent log.
46
+ */
47
+ trackedSessions = new Set();
48
+ /** `${runId}:${seq}` → childId, for `tool-workflow/agent-end` pairing. */
49
+ runSeqToChild = new Map();
50
+ constructor(ctx, callbacks) {
51
+ this.ctx = ctx;
52
+ this.callbacks = callbacks;
53
+ // Footer shows provider/model from the very first frame — read the
54
+ // composed default selection eagerly; a session created later refreshes it.
55
+ const defaultModel = ctx.get('agentDefaultModel');
56
+ const sel = defaultModel?.currentSelection();
57
+ this.selection = sel === undefined
58
+ ? undefined
59
+ : {
60
+ provider: sel.provider,
61
+ model: sel.model,
62
+ ...sel.reasoningEffort === undefined ? {} : { reasoningEffort: sel.reasoningEffort },
63
+ };
64
+ this.disposers.push(ctx.on('session/event', (session, event) => {
65
+ const sessionKey = String(session.id);
66
+ // Discover subagent children by session header — the deployment may
67
+ // never emit tool-workflow events (the firehose is not scope-filtered,
68
+ // so child sessions arrive here too). Any session whose parent is a
69
+ // tracked session is a child; the descriptor event later refines the
70
+ // label/provider.
71
+ const header = session.header;
72
+ if (header?.origin === 'subagent' && header.parentSession !== undefined
73
+ && this.trackedSessions.has(String(header.parentSession))
74
+ && !this.agentViews.has(sessionKey)) {
75
+ this.agentViews.set(sessionKey, {
76
+ childId: sessionKey,
77
+ label: `subagent ${sessionKey.slice(0, 8)}`,
78
+ startedAt: event.time,
79
+ tokens: 0,
80
+ retries: 0,
81
+ });
82
+ this.childSessions.add(sessionKey);
83
+ this.trackedSessions.add(sessionKey);
84
+ this.emitLive();
85
+ }
86
+ // Fold workflow + child events for every tracked session.
87
+ if (this.trackedSessions.has(sessionKey)) {
88
+ this.foldTracked(sessionKey, event);
89
+ }
90
+ if (this.sessionId !== undefined && session.id === this.sessionId) {
91
+ this.applyEvent(event);
92
+ this.callbacks.onEvent(event);
93
+ }
94
+ }));
95
+ this.disposers.push(ctx.on('agent/status', ({ agent, status }) => {
96
+ if (this.handle === undefined || agent.id !== this.handle.agent.id)
97
+ return;
98
+ this.running = status === 'running';
99
+ this.callbacks.onStatus(status);
100
+ }));
101
+ }
102
+ /** Snapshot of the incremental stats (footer reads this O(1)). */
103
+ getStats() {
104
+ return { ...this.stats };
105
+ }
106
+ /** Current session id, when one exists. */
107
+ getSessionId() {
108
+ return this.sessionId;
109
+ }
110
+ /** Queue one user prompt, creating the session lazily on first use. */
111
+ async prompt(text) {
112
+ const handle = await this.ensureSession();
113
+ const message = createUserMessage({
114
+ content: [{ type: 'text', text }],
115
+ source: { kind: 'user' },
116
+ });
117
+ handle.agent.followup(message);
118
+ }
119
+ /** The live agent, creating the session lazily when needed. */
120
+ async ensureAgent() {
121
+ const handle = await this.ensureSession();
122
+ return handle.agent;
123
+ }
124
+ /** The live agent when a session already exists (no creation side effect). */
125
+ getAgent() {
126
+ return this.handle?.agent;
127
+ }
128
+ /** Whether the bridge's agent is mid-turn (mirror of `agent/status`). */
129
+ isRunning() {
130
+ return this.running;
131
+ }
132
+ /**
133
+ * Abort the bridge's agent mid-turn, mirroring the web client's stop
134
+ * button: the host-side `session.cancel` handler calls
135
+ * `agent.cancel({ kind: 'user' }, { keepInbox: true })`
136
+ * (dsh-host-apiproxy), and that is exactly what the agent's public
137
+ * synchronous `cancel(cause, options)` API takes. `keepInbox` preserves
138
+ * queued prompts for a later turn; the active driver's AbortSignal fires
139
+ * and status converges to `idle` via the existing subscription. The API
140
+ * is fire-and-forget — the returned promise resolves once the call is
141
+ * made, not when the turn settles.
142
+ * @returns false when there is no agent or it is not running; true once
143
+ * the cancellation was issued.
144
+ */
145
+ async cancelActiveTurn() {
146
+ const agent = this.handle?.agent;
147
+ if (agent === undefined || !this.running)
148
+ return false;
149
+ try {
150
+ agent.cancel({ kind: 'user' }, { keepInbox: true });
151
+ return true;
152
+ }
153
+ catch {
154
+ // Cancellation must never take the input path down with it.
155
+ return false;
156
+ }
157
+ }
158
+ /** Dispose the live agent (if any) and stop event subscriptions. */
159
+ async dispose() {
160
+ for (const dispose of this.disposers.splice(0)) {
161
+ try {
162
+ dispose();
163
+ }
164
+ catch { /* contained */ }
165
+ }
166
+ const handle = this.handle;
167
+ this.handle = undefined;
168
+ this.sessionId = undefined;
169
+ this.running = false;
170
+ this.agentViews.clear();
171
+ this.childSessions.clear();
172
+ this.trackedSessions.clear();
173
+ this.runSeqToChild.clear();
174
+ if (handle !== undefined)
175
+ await handle.dispose();
176
+ }
177
+ /**
178
+ * Detach the live agent WITHOUT tearing down the event subscriptions —
179
+ * `/new` wants a fresh session while the renderer keeps receiving events.
180
+ * The agent handle is disposed, the session id cleared, and the
181
+ * incremental stats zeroed (the footer must not carry the old session's
182
+ * numbers); the disposers stay installed, and the session-id filter
183
+ * re-binds on the next lazy creation. The subagent tracker is cleared and
184
+ * an empty `onLive` is emitted so the renderer drops the agents block.
185
+ */
186
+ async detachCurrent() {
187
+ const handle = this.handle;
188
+ this.handle = undefined;
189
+ this.sessionId = undefined;
190
+ this.running = false;
191
+ this.stats.inputTokens = 0;
192
+ this.stats.outputTokens = 0;
193
+ this.stats.cacheReadTokens = 0;
194
+ this.stats.cacheWriteTokens = 0;
195
+ this.stats.msgCount = 0;
196
+ this.stats.toolCallCount = 0;
197
+ this.stats.cacheHitRate = undefined;
198
+ this.agentViews.clear();
199
+ this.childSessions.clear();
200
+ this.trackedSessions.clear();
201
+ this.runSeqToChild.clear();
202
+ this.emitLive();
203
+ if (handle !== undefined)
204
+ await handle.dispose();
205
+ }
206
+ /**
207
+ * Resume a persisted session: tear down the live agent (disposers are kept —
208
+ * the session-id filter re-binds to the resumed id) and load the persisted
209
+ * session in its place. The caller replays `handle.agent.session.events`
210
+ * through `replay()` to rebuild stats/transcript — clear the transcript
211
+ * BEFORE replaying (the renderer's echo dedupe must not see replayed user
212
+ * messages next to a stale local echo). Serialized: concurrent calls share
213
+ * the single in-flight resume.
214
+ */
215
+ async resume(sessionId) {
216
+ if (this.resuming !== undefined)
217
+ return this.resuming;
218
+ const task = (async () => {
219
+ const handle = this.handle;
220
+ this.handle = undefined;
221
+ this.sessionId = undefined;
222
+ this.running = false;
223
+ this.agentViews.clear();
224
+ this.childSessions.clear();
225
+ this.trackedSessions.clear();
226
+ this.runSeqToChild.clear();
227
+ // The renderer keeps its live snapshot across the resume teardown (it
228
+ // must survive theme-switch rebuilds), so an empty emit is required to
229
+ // drop the previous session's agents block before the resumed log's
230
+ // workflow events rebuild it.
231
+ this.emitLive();
232
+ if (handle !== undefined)
233
+ await handle.dispose();
234
+ // Same seeding rule as createSession: a live /model or /think choice
235
+ // survives the resume; otherwise the composed default applies.
236
+ this.seedSelectionFromDefault();
237
+ const resumed = await this.ctx.agents.resume({
238
+ resumeSessionId: sessionId,
239
+ agentOptions: this.selection ?? {},
240
+ // Install the mutable selection so `/model` can live-switch the route.
241
+ setup: async (agentCtx) => {
242
+ installModelSelection(agentCtx, this.selectionRef);
243
+ },
244
+ });
245
+ this.handle = resumed;
246
+ this.sessionId = sessionId;
247
+ this.trackedSessions.add(String(sessionId));
248
+ return resumed;
249
+ })();
250
+ this.resuming = task;
251
+ try {
252
+ return await task;
253
+ }
254
+ finally {
255
+ this.resuming = undefined;
256
+ }
257
+ }
258
+ /**
259
+ * Replay a persisted session log into the transcript and stats — the second
260
+ * half of a resume (call after `resume()`). Stats are rebuilt from zero so
261
+ * the footer reflects the resumed session only; replaying the same log
262
+ * twice is idempotent. `assistant/chunk` events are skipped: the finalized
263
+ * `assistant/message` carries the full text, and replaying chunks would
264
+ * re-run the quadratic streaming assembly for every historical step.
265
+ */
266
+ replay(events) {
267
+ this.stats.inputTokens = 0;
268
+ this.stats.outputTokens = 0;
269
+ this.stats.cacheReadTokens = 0;
270
+ this.stats.cacheWriteTokens = 0;
271
+ this.stats.msgCount = 0;
272
+ this.stats.toolCallCount = 0;
273
+ this.stats.cacheHitRate = undefined;
274
+ const sessionId = this.sessionId;
275
+ for (const event of events) {
276
+ if (event.type === 'assistant/chunk')
277
+ continue;
278
+ // Replay feeds the parent log only — fold its persisted workflow events
279
+ // through the same tracking logic so the agents view is rebuilt; child
280
+ // live events arrive via the subscription after resume.
281
+ if (sessionId !== undefined && this.trackedSessions.has(String(sessionId))) {
282
+ this.foldTracked(String(sessionId), event);
283
+ }
284
+ this.applyEvent(event);
285
+ this.callbacks.onEvent(event);
286
+ }
287
+ }
288
+ /** Update incremental stats from one event — one pass over the event only. */
289
+ applyEvent(event) {
290
+ switch (event.type) {
291
+ case 'user/message':
292
+ this.stats.msgCount += 1;
293
+ break;
294
+ case 'assistant/message': {
295
+ this.stats.msgCount += 1;
296
+ for (const block of event.data.message.content) {
297
+ if (block.type === 'tool-call')
298
+ this.stats.toolCallCount += 1;
299
+ }
300
+ const usage = event.data.usage;
301
+ if (usage === undefined)
302
+ break;
303
+ this.stats.inputTokens += usage.inputTokens;
304
+ this.stats.outputTokens += usage.outputTokens;
305
+ this.stats.cacheReadTokens += usage.cacheReadTokens ?? 0;
306
+ this.stats.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
307
+ const billedInput = this.stats.inputTokens + this.stats.cacheReadTokens + this.stats.cacheWriteTokens;
308
+ this.stats.cacheHitRate = billedInput > 0
309
+ ? (this.stats.cacheReadTokens / billedInput) * 100
310
+ : undefined;
311
+ break;
312
+ }
313
+ default:
314
+ break;
315
+ }
316
+ }
317
+ /**
318
+ * Fold one event of a tracked session into the agent views (workflow events
319
+ * of the parent log + the child's own log). O(1) per event — Maps/Sets only,
320
+ * never a session-log scan. Emits `onLive` when a view actually changed.
321
+ * @returns whether a view changed (and `onLive` was emitted).
322
+ */
323
+ foldTracked(sessionId, event) {
324
+ let changed = false;
325
+ if (isAgentStart(event)) {
326
+ // Workflow member: register with the workflow label + start time.
327
+ if (!this.agentViews.has(event.data.childId)) {
328
+ this.agentViews.set(event.data.childId, {
329
+ childId: event.data.childId,
330
+ label: event.data.label,
331
+ startedAt: event.time,
332
+ tokens: 0,
333
+ retries: 0,
334
+ });
335
+ this.runSeqToChild.set(`${event.data.runId}:${event.data.seq}`, event.data.childId);
336
+ this.childSessions.add(event.data.childId);
337
+ this.trackedSessions.add(event.data.childId);
338
+ changed = true;
339
+ }
340
+ }
341
+ else if (isAgentEnd(event)) {
342
+ const childId = this.runSeqToChild.get(`${event.data.runId}:${event.data.seq}`);
343
+ const existing = childId === undefined ? undefined : this.agentViews.get(childId);
344
+ if (childId !== undefined && existing !== undefined && existing.outcome === undefined) {
345
+ this.agentViews.set(childId, {
346
+ ...existing,
347
+ outcome: event.data.outcome,
348
+ endedAt: event.time,
349
+ });
350
+ changed = true;
351
+ }
352
+ }
353
+ if (this.childSessions.has(sessionId)) {
354
+ const view = this.agentViews.get(sessionId);
355
+ if (view !== undefined) {
356
+ if (isSubagentDescriptor(event)) {
357
+ if (view.provider !== event.data.provider
358
+ || (event.data.label !== undefined && view.label !== event.data.label)) {
359
+ this.agentViews.set(sessionId, {
360
+ ...view,
361
+ provider: event.data.provider,
362
+ ...(event.data.label === undefined ? {} : { label: event.data.label }),
363
+ });
364
+ changed = true;
365
+ }
366
+ }
367
+ else if (event.type === 'assistant/message') {
368
+ const usage = event.data.usage;
369
+ if (usage !== undefined) {
370
+ const delta = usage.inputTokens + usage.outputTokens
371
+ + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
372
+ if (delta > 0) {
373
+ this.agentViews.set(sessionId, { ...view, tokens: view.tokens + delta });
374
+ changed = true;
375
+ }
376
+ }
377
+ }
378
+ else if (isLlmRetry(event)) {
379
+ if (view.retries !== event.data.retry || view.maxRetries !== event.data.maxRetries) {
380
+ this.agentViews.set(sessionId, {
381
+ ...view,
382
+ retries: event.data.retry,
383
+ maxRetries: event.data.maxRetries,
384
+ });
385
+ changed = true;
386
+ }
387
+ }
388
+ else if (event.type === 'tool/call') {
389
+ if (view.lastTool !== event.data.name) {
390
+ this.agentViews.set(sessionId, { ...view, lastTool: event.data.name });
391
+ changed = true;
392
+ }
393
+ }
394
+ else if (event.type === 'request/context') {
395
+ const contextWindow = event.data.contextWindow;
396
+ if (typeof contextWindow === 'number' && view.contextWindow !== contextWindow) {
397
+ this.agentViews.set(sessionId, { ...view, contextWindow });
398
+ changed = true;
399
+ }
400
+ }
401
+ else if (event.type === 'turn/end' && view.outcome === undefined) {
402
+ // Best-effort settle for header-discovered children (no workflow
403
+ // outcome event): the child's turn closed — the widget drops it.
404
+ this.agentViews.set(sessionId, { ...view, outcome: 'completed', endedAt: event.time });
405
+ changed = true;
406
+ }
407
+ else if (event.type === 'turn/start' && view.outcome !== undefined) {
408
+ // A continuable child resumed: mark it running again.
409
+ this.agentViews.set(sessionId, { ...view, outcome: undefined, endedAt: undefined });
410
+ changed = true;
411
+ }
412
+ }
413
+ }
414
+ if (changed)
415
+ this.emitLive();
416
+ return changed;
417
+ }
418
+ /** Push the current agent views (stable order) to the renderer. */
419
+ emitLive() {
420
+ this.callbacks.onLive([...this.agentViews.values()].sort((a, b) => a.startedAt - b.startedAt || a.childId.localeCompare(b.childId)));
421
+ }
422
+ async ensureSession() {
423
+ // A resume in flight would otherwise race this check: the handle is only
424
+ // set when resume completes, so a prompt fired mid-resume must not start
425
+ // a second agent creation. Await it — on success the handle is ready for
426
+ // reuse; on failure fall through to the normal creation path.
427
+ if (this.resuming !== undefined) {
428
+ try {
429
+ await this.resuming;
430
+ }
431
+ catch {
432
+ // Resume failed — fall through to create a fresh session.
433
+ }
434
+ }
435
+ if (this.handle !== undefined)
436
+ return this.handle;
437
+ if (this.creating !== undefined)
438
+ return this.creating;
439
+ const creation = this.createSession();
440
+ this.creating = creation;
441
+ try {
442
+ this.handle = await creation;
443
+ this.sessionId = this.handle.agent.session.id;
444
+ this.trackedSessions.add(String(this.sessionId));
445
+ return this.handle;
446
+ }
447
+ finally {
448
+ this.creating = undefined;
449
+ }
450
+ }
451
+ /**
452
+ * Seed `selection`/`selectionRef` from the composed default — without
453
+ * clobbering a live user choice: `/model`/`/think` before the first prompt
454
+ * write the ref, and that choice must survive session creation.
455
+ */
456
+ seedSelectionFromDefault() {
457
+ if (this.selectionRef.current !== undefined) {
458
+ this.selection = { ...this.selectionRef.current };
459
+ return;
460
+ }
461
+ const defaultModel = this.ctx.get('agentDefaultModel');
462
+ const selection = defaultModel?.currentSelection();
463
+ this.selection = selection === undefined
464
+ ? undefined
465
+ : {
466
+ provider: selection.provider,
467
+ model: selection.model,
468
+ ...selection.reasoningEffort === undefined ? {} : { reasoningEffort: selection.reasoningEffort },
469
+ };
470
+ this.selectionRef.current = this.selection === undefined ? undefined : { ...this.selection };
471
+ }
472
+ /** Create the agent with the composed default model selection. */
473
+ createSession() {
474
+ this.seedSelectionFromDefault();
475
+ return this.ctx.agents.create({
476
+ sessionId: SessionId(crypto.randomUUID()),
477
+ meta: { cwd: process.cwd() },
478
+ agentOptions: this.selection ?? {},
479
+ // Install the mutable selection so `/model` can live-switch the route.
480
+ setup: async (agentCtx) => {
481
+ installModelSelection(agentCtx, this.selectionRef);
482
+ },
483
+ });
484
+ }
485
+ /** The model selection shown in the footer (live value after `/model`). */
486
+ getSelection() {
487
+ return this.selectionRef.current ?? this.selection;
488
+ }
489
+ /** Apply a live model switch (`/model` selector outcome). */
490
+ setSelection(next) {
491
+ this.selectionRef.current = { ...next };
492
+ this.selection = { ...next };
493
+ }
494
+ }
495
+ /**
496
+ * Persist the default model selection. Preferred path: the
497
+ * `agent-default-model` service's `saveSelection`, the official API (a
498
+ * settings.replace, last-write-wins). The namespace is read live by that
499
+ * service — the change takes effect on the next lazy session creation
500
+ * (`/new` or the next prompt) and survives restarts. Without the service,
501
+ * fall back to a direct settings mutate (with one optimistic-concurrency
502
+ * retry). Best-effort: a deployment without a settings provider is skipped
503
+ * silently; a failed write returns its error message for the caller to
504
+ * surface.
505
+ * @returns undefined on success, the failure message otherwise.
506
+ */
507
+ export async function persistDefaultModel(ctx, selection) {
508
+ const service = ctx.get('agentDefaultModel');
509
+ if (service?.saveSelection !== undefined) {
510
+ try {
511
+ // The service API takes the whole selection and writes through
512
+ // settings.replace (last-write-wins, no revision check). It may be
513
+ // sync or async — awaiting covers both.
514
+ await service.saveSelection(selection);
515
+ return undefined;
516
+ }
517
+ catch (error) {
518
+ return error instanceof Error ? error.message : String(error);
519
+ }
520
+ }
521
+ const settings = ctx.get('settings');
522
+ if (settings === undefined)
523
+ return undefined;
524
+ const ns = settingsNamespace('agent-default-model');
525
+ // The descriptor carries the namespace's revision (optimistic-concurrency
526
+ // token for mutate) and proves the schema registration that validates the
527
+ // field names below; the write itself rejects when the namespace is
528
+ // unregistered (no descriptor either).
529
+ const ops = [
530
+ { op: 'set', path: ['provider'], value: selection.provider },
531
+ { op: 'set', path: ['model'], value: selection.model },
532
+ ];
533
+ if (selection.reasoningEffort === undefined) {
534
+ // Unset so a stale override is dropped from the stored yaml document too.
535
+ ops.push({ op: 'unset', path: ['reasoningEffort'] });
536
+ }
537
+ else {
538
+ ops.push({ op: 'set', path: ['reasoningEffort'], value: selection.reasoningEffort });
539
+ }
540
+ // One retry after a concurrent writer moved the namespace (fresh revision);
541
+ // a second conflict surfaces the raw error for the caller.
542
+ for (let attempt = 0;; attempt++) {
543
+ const descriptor = settings.describe().find(d => d.ns === ns);
544
+ try {
545
+ await settings.mutate(ns, ops, descriptor?.revision);
546
+ return undefined;
547
+ }
548
+ catch (error) {
549
+ if (attempt === 0 && error instanceof SettingsConflictError)
550
+ continue;
551
+ return error instanceof Error ? error.message : String(error);
552
+ }
553
+ }
554
+ }
555
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Session TUI overlays: the `/session` info panel and the `/resume`
3
+ * persisted-session picker — terminal counterparts of the web surface's
4
+ * session management. Both follow the pi SelectList overlay pattern
5
+ * (`showOverlay` + `restoreFocus` on close); the info panel is a one-shot
6
+ * read-only component in the style of settings' ReadOnlyViewer.
7
+ */
8
+ import type { Context } from '@deepseek-ai/cordis';
9
+ import type { SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session';
10
+ import { type TUI } from '@earendil-works/pi-tui';
11
+ import { type TuiTheme } from './theme/index.ts';
12
+ /** Everything the `/session` info panel renders (snapshot, built by the caller). */
13
+ export interface SessionPanelData {
14
+ id: string | undefined;
15
+ cwd: string | undefined;
16
+ createdAt: number | undefined;
17
+ /** "provider/model", or already including the reasoning effort. */
18
+ model: string | undefined;
19
+ effort: string | undefined;
20
+ msgCount: number;
21
+ toolCallCount: number;
22
+ inputTokens: number;
23
+ outputTokens: number;
24
+ cacheReadTokens: number;
25
+ cacheWriteTokens: number;
26
+ status: 'idle' | 'running' | 'none';
27
+ eventCount: number | undefined;
28
+ parentSession: string | undefined;
29
+ }
30
+ /**
31
+ * Open the `/session` info panel; resolves when the user closes it. Focus
32
+ * returns to `restoreFocus` on close — it must re-focus the CURRENT editor
33
+ * instance, which may have been rebuilt under a theme hot-swap while the
34
+ * panel was open (pi-tui's hide would otherwise restore focus to the stale
35
+ * pre-overlay editor and swallow subsequent input).
36
+ */
37
+ export declare function showSessionInfo(tui: TUI, theme: TuiTheme, data: SessionPanelData, restoreFocus: () => void): Promise<void>;
38
+ /**
39
+ * The session's "first sentence": the first direct human prompt, falling
40
+ * back to the first non-injected user-role text (goal/cron/recall prompts),
41
+ * then the first assistant message. Synthetic context injects (workspace
42
+ * instructions, runtime reminders) are skipped entirely — they precede the
43
+ * first real prompt and read as noise.
44
+ */
45
+ export declare function previewOfEvents(events: readonly SessionEvent[]): string | undefined;
46
+ /** Collapse control chars/whitespace and clip a raw message into a one-line preview. */
47
+ export declare function normalizePreview(text: string, maxChars?: number): string;
48
+ /**
49
+ * Validate one persisted session's log without publishing it — the /resume
50
+ * pre-check: a corrupt target must be rejected BEFORE the current live agent
51
+ * is torn down. Throws when persistence is missing or the log fails to load.
52
+ */
53
+ export declare function inspectPersistedSession(ctx: Context, id: SessionId): Promise<{
54
+ meta: SessionHeader;
55
+ events: readonly SessionEvent[];
56
+ }>;
57
+ /** Outcome of the `/resume` picker, so the caller can phrase its reply. */
58
+ export type PickSessionResult = {
59
+ kind: 'empty';
60
+ } | {
61
+ kind: 'cancelled';
62
+ } | {
63
+ kind: 'picked';
64
+ id: SessionId;
65
+ header: SessionHeader;
66
+ };
67
+ /**
68
+ * Open the persisted-session picker. Resolves with the picked session,
69
+ * `cancelled` when dismissed, or `empty` when no other session exists.
70
+ * Throws when the profile has no persistence backend. Focus returns to
71
+ * `restoreFocus` on close.
72
+ */
73
+ export declare function pickPersistedSession(ctx: Context, tui: TUI, theme: TuiTheme, excludeSessionId: string | undefined, restoreFocus: () => void): Promise<PickSessionResult>;