@timurproko/a1 0.2.7-dev.1255 → 0.2.7-dev.1265

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 (81) hide show
  1. package/dist/app/session-shell/prompt-submission.d.ts +1 -1
  2. package/dist/app/session-shell/prompt-submission.js +9 -9
  3. package/dist/app/session-shell/resource-labels.d.ts +2 -0
  4. package/dist/app/session-shell/resource-labels.js +158 -0
  5. package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
  6. package/dist/app/session-shell/session-prompt-history.js +80 -0
  7. package/dist/app/session-shell/session-selectors.d.ts +46 -0
  8. package/dist/app/session-shell/session-selectors.js +269 -0
  9. package/dist/app/session-shell/session-shell-root.d.ts +6 -28
  10. package/dist/app/session-shell/session-shell-root.js +26 -220
  11. package/dist/app/session-shell/session-shell.d.ts +3 -23
  12. package/dist/app/session-shell/session-shell.js +70 -388
  13. package/dist/app/session-shell/session-terminal-presentation.d.ts +16 -0
  14. package/dist/app/session-shell/session-terminal-presentation.js +35 -0
  15. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -10
  16. package/dist/app/session-shell/session-viewport-controller.js +40 -318
  17. package/dist/app/session-shell/settings-bindings.d.ts +1 -1
  18. package/dist/app/session-shell/settings-bindings.js +2 -2
  19. package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
  20. package/dist/app/session-shell/viewport-pointer.js +361 -0
  21. package/dist/composition/owned-ui.js +63 -59
  22. package/dist/contracts/owned-ui/index.d.ts +1 -1
  23. package/dist/contracts/owned-ui/index.js +1 -1
  24. package/dist/contracts/owned-ui/validation.d.ts +1 -11
  25. package/dist/contracts/owned-ui/validation.js +8 -104
  26. package/dist/contracts/presentation/index.d.ts +1 -1
  27. package/dist/contracts/presentation/terminal.d.ts +0 -6
  28. package/dist/features/owned-ui/settings-app.js +13 -208
  29. package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
  30. package/dist/features/owned-ui/settings-model-dialog.js +194 -0
  31. package/dist/foundation/release/automatic-update.js +1 -8
  32. package/dist/foundation/release/dependency-layer.js +3 -3
  33. package/dist/foundation/release/latest-release.js +1 -1
  34. package/dist/foundation/release/managed-path.d.ts +10 -0
  35. package/dist/foundation/release/managed-path.js +26 -0
  36. package/dist/foundation/release/release-gc.js +6 -8
  37. package/dist/foundation/release/restart-certification.js +3 -4
  38. package/dist/foundation/release/update-activation.js +1 -1
  39. package/dist/foundation/release/update-execution-lease.js +1 -7
  40. package/dist/foundation/release/update-installer.d.ts +45 -0
  41. package/dist/foundation/release/update-installer.js +57 -0
  42. package/dist/foundation/release/update-recovery.js +2 -9
  43. package/dist/foundation/release/update.d.ts +4 -23
  44. package/dist/foundation/release/update.js +160 -179
  45. package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
  46. package/dist/integrations/pi/components/bracketed-paste.js +123 -0
  47. package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
  48. package/dist/integrations/pi/engine/adapter.d.ts +1 -8
  49. package/dist/integrations/pi/engine/adapter.js +6 -49
  50. package/dist/integrations/pi/engine/host.js +1 -1
  51. package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
  52. package/dist/integrations/pi/engine/repository-metadata.js +189 -0
  53. package/dist/integrations/pi/engine/session-events.js +8 -6
  54. package/dist/integrations/pi/engine/session-runtime.d.ts +6 -12
  55. package/dist/integrations/pi/engine/session-runtime.js +21 -178
  56. package/dist/integrations/pi/engine/view-state.d.ts +21 -2
  57. package/dist/integrations/pi/engine/view-state.js +129 -1
  58. package/dist/integrations/pi/engine/workflow-runner.d.ts +0 -1
  59. package/dist/integrations/pi/engine/workflow-runner.js +254 -365
  60. package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
  61. package/dist/integrations/pi/engine/workflow-share.js +168 -0
  62. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -14
  63. package/dist/integrations/pi/engine/workflow-support.js +0 -47
  64. package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
  65. package/dist/integrations/pi/tui-runtime/adapter.js +35 -144
  66. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +4 -4
  67. package/dist/integrations/pi/tui-runtime/index.d.ts +0 -1
  68. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -2
  69. package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
  70. package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
  71. package/dist/ui/components/transcript-viewport-frame.js +314 -0
  72. package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
  73. package/dist/ui/components/transcript-viewport.d.ts +2 -147
  74. package/dist/ui/components/transcript-viewport.js +87 -315
  75. package/package.json +3 -3
  76. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
  77. package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
  78. package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
  79. package/dist/integrations/pi/engine/usage-view.js +0 -56
  80. package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -22
  81. /package/dist/{integrations/pi/tui-runtime/contracts.js → ui/components/transcript-viewport-types.js} +0 -0
@@ -5,7 +5,8 @@ import type { PiProjectTrustPreflightPrompt } from "./project-trust-preflight.js
5
5
  import type { PiSessionForkPrompt, PiSessionSelection } from "./session-selection.js";
6
6
  import type { OwnedUiDiagnostics } from "../../../contracts/owned-ui/index.js";
7
7
  import type { PiSessionResumeMetadata, PiWorkflowHost } from "./workflows.js";
8
- import { type PiPullRequestIdentity, type PiPullRequestProbe } from "./repository-pr.js";
8
+ import type { PiPullRequestProbe } from "./repository-pr.js";
9
+ import { RepositoryMetadataPoller, type PiRepositoryContextReader } from "./repository-metadata.js";
9
10
  export interface PiEngineRuntimeFactoryInput {
10
11
  readonly cwd: string;
11
12
  readonly agentDir: string;
@@ -20,12 +21,7 @@ export interface PiEngineRuntimeFactoryInput {
20
21
  }
21
22
  export type PiEngineRuntimeFactory = (input: PiEngineRuntimeFactoryInput) => Promise<AgentSessionRuntime>;
22
23
  export type PiEnginePackageUpdateProbe = (settingsManager: AgentSessionServices["settingsManager"]) => Promise<readonly string[]>;
23
- interface PiRepositoryContextSelection {
24
- readonly cwd: string;
25
- readonly branch: string;
26
- }
27
- export type PiRepositoryContextReader = (sessionId: string, sessionFile: string, signal: AbortSignal) => Promise<PiRepositoryContextSelection | null>;
28
- type PiGitBranchReader = (cwd: string, signal: AbortSignal) => Promise<string | null>;
24
+ export type { PiRepositoryContextReader } from "./repository-metadata.js";
29
25
  interface PiEngineRuntimeOptions {
30
26
  readonly cwd: string;
31
27
  readonly agentDir: string;
@@ -43,7 +39,7 @@ interface PiEngineRuntimeOptions {
43
39
  readonly pullRequestRefreshMs?: number;
44
40
  readonly repositoryContextPollMs?: number;
45
41
  readonly repositoryContextReader?: PiRepositoryContextReader;
46
- readonly gitBranchReader?: PiGitBranchReader;
42
+ readonly gitBranchReader?: (cwd: string, signal: AbortSignal) => Promise<string | null>;
47
43
  readonly host: PiWorkflowHost;
48
44
  }
49
45
  export interface PiEngineRuntimePorts {
@@ -74,9 +70,8 @@ export declare class PiEngineRuntime {
74
70
  /** Actual session replacements, excluding delivery-only invalidation of callbacks. */
75
71
  get bindingGeneration(): number;
76
72
  get cwd(): string;
77
- get repositoryCwd(): string;
78
- get gitBranch(): string | null;
79
- get pullRequest(): PiPullRequestIdentity | null;
73
+ /** The branch and pull request polled for the bound session. */
74
+ get repository(): RepositoryMetadataPoller;
80
75
  get sessionCommands(): PiSessionCommandIntegration | undefined;
81
76
  get started(): boolean;
82
77
  /** Create the runtime and bind its first session; startup failures propagate. */
@@ -106,4 +101,3 @@ export declare class PiEngineRuntime {
106
101
  */
107
102
  announceChangelog(): Promise<void>;
108
103
  }
109
- export {};
@@ -1,17 +1,10 @@
1
- import { execFile } from "node:child_process";
2
1
  import { existsSync } from "node:fs";
3
- import { promisify } from "node:util";
4
2
  import { PRODUCT_IDENTITY } from "../../../product-identity.js";
5
3
  import { observeCompactionProgress } from "./compaction-progress.js";
6
4
  import { createPiRuntimeIntegration } from "./runtime-integration.js";
7
5
  import { PiSessionCommandIntegration } from "./session-integration.js";
8
6
  import { DefaultPackageManager, VERSION } from "../startup-public.js";
9
- import { readPullRequest, } from "./repository-pr.js";
10
- const execFileAsync = promisify(execFile);
11
- const PULL_REQUEST_REFRESH_MS = 60_000;
12
- const REPOSITORY_CONTEXT_POLL_MS = 5_000;
13
- const GIT_BRANCH_TIMEOUT_MS = 5_000;
14
- const GIT_BRANCH_MAX_BUFFER_BYTES = 4 * 1024;
7
+ import { RepositoryMetadataPoller } from "./repository-metadata.js";
15
8
  /**
16
9
  * The pinned Pi runtime and the session bound to it: creation through the runtime factory, the
17
10
  * session generation that stamps and invalidates events, the subscription that forwards session
@@ -26,11 +19,6 @@ export class PiEngineRuntime {
26
19
  #ports;
27
20
  #runtimeFactory;
28
21
  #checkPackageUpdates;
29
- #pullRequestProbe;
30
- #pullRequestRefreshMs;
31
- #repositoryContextPollMs;
32
- #repositoryContextReader;
33
- #gitBranchReader;
34
22
  #cwd;
35
23
  #runtime;
36
24
  #session;
@@ -39,31 +27,27 @@ export class PiEngineRuntime {
39
27
  #sessionBindingGeneration = 0;
40
28
  #sessionCommands;
41
29
  #compactionProgress = null;
42
- #repositoryCwd;
43
- #gitBranch = null;
44
- #pullRequest = null;
45
- #repositoryRefreshTimer;
46
- #repositoryRefreshAbort;
47
- #repositoryRefreshTask;
48
- #repositoryRefreshPending = false;
49
- #lastPullRequestProbeAt = null;
50
- #repositoryRefreshEnabled = false;
30
+ #repository;
51
31
  #disposed = false;
52
32
  constructor(options, ports) {
53
33
  this.#options = options;
54
34
  this.#ports = ports;
55
35
  this.#cwd = options.cwd;
56
- this.#repositoryCwd = options.cwd;
57
36
  this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
58
37
  this.#checkPackageUpdates = options.checkPackageUpdates
59
38
  ?? (options.createRuntime
60
39
  ? async () => []
61
40
  : settingsManager => checkDefaultPiPackageUpdates(this.#cwd, options.agentDir, settingsManager));
62
- this.#pullRequestProbe = options.pullRequestProbe ?? readPullRequest;
63
- this.#pullRequestRefreshMs = options.pullRequestRefreshMs ?? PULL_REQUEST_REFRESH_MS;
64
- this.#repositoryContextPollMs = options.repositoryContextPollMs ?? REPOSITORY_CONTEXT_POLL_MS;
65
- this.#repositoryContextReader = options.repositoryContextReader ?? (async () => null);
66
- this.#gitBranchReader = options.gitBranchReader ?? readGitBranch;
41
+ this.#repository = new RepositoryMetadataPoller(options, {
42
+ bindingGeneration: () => this.#sessionBindingGeneration,
43
+ sessionIdentity: () => {
44
+ const manager = this.#session?.sessionManager;
45
+ return { sessionId: manager?.getSessionId?.(), sessionFile: manager?.getSessionFile?.() };
46
+ },
47
+ fallbackCwd: () => this.#cwd,
48
+ disposed: () => this.#disposed || this.#ports.disposed(),
49
+ changed: () => this.#ports.emitView(),
50
+ });
67
51
  }
68
52
  get runtime() {
69
53
  return this.#runtime;
@@ -82,14 +66,9 @@ export class PiEngineRuntime {
82
66
  get cwd() {
83
67
  return this.#runtime?.cwd ?? this.#cwd;
84
68
  }
85
- get repositoryCwd() {
86
- return this.#repositoryCwd;
87
- }
88
- get gitBranch() {
89
- return this.#gitBranch;
90
- }
91
- get pullRequest() {
92
- return this.#pullRequest;
69
+ /** The branch and pull request polled for the bound session. */
70
+ get repository() {
71
+ return this.#repository;
93
72
  }
94
73
  get sessionCommands() {
95
74
  return this.#sessionCommands;
@@ -112,7 +91,7 @@ export class PiEngineRuntime {
112
91
  });
113
92
  this.#runtime = runtime;
114
93
  this.#cwd = runtime.cwd ?? this.#cwd;
115
- this.#repositoryCwd = this.#cwd;
94
+ this.#repository.reset();
116
95
  this.#ports.started(runtime);
117
96
  runtime.setRebindSession(async (session) => {
118
97
  if (this.#ports.rebindBlocked())
@@ -127,8 +106,7 @@ export class PiEngineRuntime {
127
106
  this.#ports.diagnostic(diagnostic.type === "error" ? "error" : diagnostic.type === "warning" ? "warning" : "info", code, diagnostic.message, diagnostic.type !== "error");
128
107
  }
129
108
  this.bindSession(runtime.session);
130
- this.#repositoryRefreshEnabled = true;
131
- this.#startRepositoryRefresh();
109
+ this.#repository.start();
132
110
  return runtime;
133
111
  }
134
112
  /**
@@ -186,10 +164,9 @@ export class PiEngineRuntime {
186
164
  // Invariant: the subscription is live before the adapter rebuilds its state so the extension
187
165
  // rebind that finishes the rebuild cannot emit an event nobody is listening to.
188
166
  this.#subscribe();
189
- this.#resetRepositoryRefresh();
167
+ this.#repository.reset();
190
168
  this.#ports.sessionReplaced(session);
191
- if (this.#repositoryRefreshEnabled)
192
- this.#startRepositoryRefresh();
169
+ this.#repository.refresh();
193
170
  }
194
171
  /** Stop forwarding the current session's events and open a new generation; overload recovery resumes or abandons it. */
195
172
  suspend() {
@@ -235,127 +212,14 @@ export class PiEngineRuntime {
235
212
  /** Unsubscribe, stop observing compaction, and dispose the runtime; the session reference is kept for final reads. */
236
213
  async dispose() {
237
214
  this.#disposed = true;
238
- this.#repositoryRefreshEnabled = false;
239
- if (this.#repositoryRefreshTimer !== undefined)
240
- clearTimeout(this.#repositoryRefreshTimer);
241
- this.#repositoryRefreshTimer = undefined;
242
- this.#repositoryRefreshPending = false;
243
- this.#repositoryRefreshAbort?.abort();
244
- const repositoryRefreshTask = this.#repositoryRefreshTask;
215
+ const repository = this.#repository.dispose();
245
216
  this.#unsubscribe?.();
246
217
  this.#unsubscribe = undefined;
247
218
  this.#compactionProgress?.dispose();
248
219
  this.#compactionProgress = null;
249
- await repositoryRefreshTask;
220
+ await repository;
250
221
  await this.#runtime?.dispose();
251
222
  }
252
- #startRepositoryRefresh() {
253
- if (!this.#repositoryRefreshEnabled || this.#disposed || this.#ports.disposed())
254
- return;
255
- if (this.#repositoryRefreshTask !== undefined) {
256
- this.#repositoryRefreshPending = true;
257
- return;
258
- }
259
- const task = this.#refreshRepositoryMetadata();
260
- this.#repositoryRefreshTask = task;
261
- const settled = () => {
262
- if (this.#repositoryRefreshTask === task)
263
- this.#repositoryRefreshTask = undefined;
264
- if (!this.#repositoryRefreshPending)
265
- return;
266
- this.#repositoryRefreshPending = false;
267
- this.#startRepositoryRefresh();
268
- };
269
- void task.then(settled, settled);
270
- }
271
- #resetRepositoryRefresh() {
272
- if (this.#repositoryRefreshTimer !== undefined)
273
- clearTimeout(this.#repositoryRefreshTimer);
274
- this.#repositoryRefreshTimer = undefined;
275
- this.#repositoryRefreshAbort?.abort();
276
- this.#repositoryCwd = this.#cwd;
277
- this.#gitBranch = null;
278
- this.#pullRequest = null;
279
- this.#lastPullRequestProbeAt = null;
280
- }
281
- async #refreshRepositoryMetadata() {
282
- if (this.#repositoryRefreshAbort !== undefined || this.#disposed || this.#ports.disposed())
283
- return;
284
- const controller = new AbortController();
285
- const generation = this.#sessionBindingGeneration;
286
- this.#repositoryRefreshAbort = controller;
287
- let associatedContext = null;
288
- const manager = this.#session?.sessionManager;
289
- const sessionId = manager?.getSessionId?.();
290
- const sessionFile = manager?.getSessionFile?.();
291
- try {
292
- if (typeof sessionId === "string" && sessionId.length > 0 && typeof sessionFile === "string" && sessionFile.length > 0) {
293
- try {
294
- associatedContext = await this.#repositoryContextReader(sessionId, sessionFile, controller.signal);
295
- }
296
- catch {
297
- associatedContext = null;
298
- }
299
- }
300
- const nextCwd = associatedContext?.cwd ?? this.#cwd;
301
- let nextBranch = associatedContext?.branch ?? null;
302
- if (associatedContext === null) {
303
- try {
304
- nextBranch = await this.#gitBranchReader(nextCwd, controller.signal);
305
- }
306
- catch {
307
- nextBranch = null;
308
- }
309
- }
310
- const metadataChanged = nextCwd !== this.#repositoryCwd || nextBranch !== this.#gitBranch;
311
- const remoteDue = metadataChanged || this.#lastPullRequestProbeAt === null
312
- || Date.now() - this.#lastPullRequestProbeAt >= this.#pullRequestRefreshMs;
313
- if (remoteDue) {
314
- let nextPullRequest = null;
315
- try {
316
- nextPullRequest = nextBranch === null
317
- ? null
318
- : await this.#pullRequestProbe(nextCwd, nextBranch, controller.signal);
319
- }
320
- catch {
321
- nextPullRequest = null;
322
- }
323
- if (controller.signal.aborted || generation !== this.#sessionBindingGeneration || this.#disposed || this.#ports.disposed())
324
- return;
325
- const changed = nextCwd !== this.#repositoryCwd
326
- || nextBranch !== this.#gitBranch
327
- || !samePullRequest(this.#pullRequest, nextPullRequest);
328
- this.#repositoryCwd = nextCwd;
329
- this.#gitBranch = nextBranch;
330
- this.#pullRequest = nextPullRequest;
331
- this.#lastPullRequestProbeAt = Date.now();
332
- if (changed)
333
- this.#ports.emitView();
334
- }
335
- }
336
- catch {
337
- if (!controller.signal.aborted && generation === this.#sessionBindingGeneration && !this.#disposed && !this.#ports.disposed()) {
338
- const changed = this.#repositoryCwd !== this.#cwd || this.#gitBranch !== null || this.#pullRequest !== null;
339
- this.#repositoryCwd = this.#cwd;
340
- this.#gitBranch = null;
341
- this.#pullRequest = null;
342
- this.#lastPullRequestProbeAt = Date.now();
343
- if (changed)
344
- this.#ports.emitView();
345
- }
346
- }
347
- finally {
348
- if (this.#repositoryRefreshAbort === controller)
349
- this.#repositoryRefreshAbort = undefined;
350
- }
351
- if (controller.signal.aborted || generation !== this.#sessionBindingGeneration || this.#disposed || this.#ports.disposed())
352
- return;
353
- this.#repositoryRefreshTimer = setTimeout(() => {
354
- this.#repositoryRefreshTimer = undefined;
355
- this.#startRepositoryRefresh();
356
- }, this.#repositoryContextPollMs);
357
- this.#repositoryRefreshTimer.unref?.();
358
- }
359
223
  #attachCompactionProgress(session) {
360
224
  this.#compactionProgress?.dispose();
361
225
  this.#compactionProgress = observeCompactionProgress(session, percent => {
@@ -419,24 +283,3 @@ async function checkDefaultPiPackageUpdates(cwd, agentDir, settingsManager) {
419
283
  const updates = await packageManager.checkForAvailableUpdates();
420
284
  return updates.map(update => update.displayName);
421
285
  }
422
- function samePullRequest(left, right) {
423
- return left?.number === right?.number && left?.url === right?.url;
424
- }
425
- async function readGitBranch(cwd, signal) {
426
- if (!existsSync(cwd))
427
- return null;
428
- try {
429
- const { stdout } = await execFileAsync("git", ["branch", "--show-current"], {
430
- cwd,
431
- windowsHide: true,
432
- timeout: GIT_BRANCH_TIMEOUT_MS,
433
- maxBuffer: GIT_BRANCH_MAX_BUFFER_BYTES,
434
- signal,
435
- encoding: "utf8",
436
- });
437
- return stdout.trim() || null;
438
- }
439
- catch {
440
- return null;
441
- }
442
- }
@@ -1,5 +1,13 @@
1
- import { EngineDiagnostics } from "./diagnostics-state.js";
2
- import type { OwnedUiDiagnostics, OwnedUiEditorState, OwnedUiModelInfo, OwnedUiSessionViewModel, OwnedUiStatusView, OwnedUiTerminalSurface, OwnedUiThinkingLevel, OwnedUiUsageView } from "../../../contracts/owned-ui/index.js";
1
+ import type { PiEngineRuntime } from "./session-runtime.js";
2
+ import type { OwnedUiDiagnostics, OwnedUiEditorState, OwnedUiModelInfo, OwnedUiSessionViewModel, OwnedUiStatusView, OwnedUiTerminalSurface, OwnedUiThinkingLevel, OwnedUiTranscriptBlock, OwnedUiUsageView } from "../../../contracts/owned-ui/index.js";
3
+ /** Bounded structured notices; acknowledgements never inspect terminal cells or touch Agent work. */
4
+ declare class EngineDiagnostics {
5
+ #private;
6
+ get values(): readonly OwnedUiDiagnostics[];
7
+ get messages(): readonly string[];
8
+ record(severity: OwnedUiDiagnostics["severity"], code: string, message: string, recoverable: boolean): OwnedUiDiagnostics;
9
+ clearCodes(codes: readonly string[]): boolean;
10
+ }
3
11
  /**
4
12
  * The engine adapter's own view-model state: lifecycle, editor, status, terminal surface, active
5
13
  * model, thinking level, the usage cache, diagnostics, and the view revision. Every change that
@@ -41,3 +49,14 @@ export declare class EngineViewState {
41
49
  /** Delivery overload ended the run: ready when it can resume, stopped once disposed, otherwise failed. */
42
50
  settleOverload(canResume: boolean, disposed: boolean): void;
43
51
  }
52
+ /** What the session view is projected from: the adapter's view state, the bound engine, the transcript and the running commands. */
53
+ export interface PiSessionViewSource {
54
+ readonly sessionId: string;
55
+ readonly state: EngineViewState;
56
+ readonly engine: PiEngineRuntime;
57
+ readonly transcript: readonly OwnedUiTranscriptBlock[];
58
+ readonly activeCommandIds: readonly string[];
59
+ }
60
+ /** The owned UI session view model, copied so a published view never aliases adapter state. */
61
+ export declare function projectSessionView({ sessionId, state, engine, transcript, activeCommandIds }: PiSessionViewSource): OwnedUiSessionViewModel;
62
+ export {};
@@ -1,4 +1,38 @@
1
- import { EngineDiagnostics } from "./diagnostics-state.js";
1
+ import { finiteNumber, isRecord, readModel, readThinkingLevel } from "./message-values.js";
2
+ /** Bounded structured notices; acknowledgements never inspect terminal cells or touch Agent work. */
3
+ class EngineDiagnostics {
4
+ #values = [];
5
+ #messages = [];
6
+ #sequence = 0;
7
+ get values() { return this.#values; }
8
+ get messages() { return this.#messages; }
9
+ record(severity, code, message, recoverable) {
10
+ const diagnostic = { sequence: this.#sequence++, code, severity, message, recoverable };
11
+ this.#values.push(diagnostic);
12
+ if (this.#values.length > 100)
13
+ this.#values.shift();
14
+ this.#messages = [...this.#messages, message].slice(-8);
15
+ return diagnostic;
16
+ }
17
+ clearCodes(codes) {
18
+ const selected = new Set(codes);
19
+ return this.#remove(diagnostic => selected.has(diagnostic.code));
20
+ }
21
+ #remove(predicate) {
22
+ const removed = new Set();
23
+ for (let index = this.#values.length - 1; index >= 0; index--) {
24
+ const diagnostic = this.#values[index];
25
+ if (predicate(diagnostic)) {
26
+ removed.add(diagnostic.message);
27
+ this.#values.splice(index, 1);
28
+ }
29
+ }
30
+ if (removed.size === 0)
31
+ return false;
32
+ this.#messages = this.#messages.filter(message => !removed.has(message) || this.#values.some(diagnostic => diagnostic.message === message));
33
+ return true;
34
+ }
35
+ }
2
36
  const DEFAULT_SURFACE = {
3
37
  columns: 100,
4
38
  rows: 32,
@@ -112,3 +146,97 @@ export class EngineViewState {
112
146
  this.#revision += 1;
113
147
  }
114
148
  }
149
+ /** Token, cost, cache, and context usage summed from the session's entries for the status footer. */
150
+ function readUsageView(session, runtime, activeModel) {
151
+ let input = 0;
152
+ let output = 0;
153
+ let cacheRead = 0;
154
+ let cacheWrite = 0;
155
+ let cost = 0;
156
+ let latestCacheHitRate = null;
157
+ let latestPrompt = null;
158
+ const entries = session?.sessionManager?.getEntries?.()
159
+ ?? (session?.messages ?? []).map(message => ({ type: "message", message }));
160
+ for (const entry of entries) {
161
+ if (!isRecord(entry))
162
+ continue;
163
+ const message = entry.type === "message" && isRecord(entry.message) ? entry.message : undefined;
164
+ const usage = message !== undefined && isRecord(message.usage)
165
+ ? message.usage
166
+ : (entry.type === "usage" || entry.type === "branch_summary" || entry.type === "compaction") && isRecord(entry.usage) ? entry.usage : undefined;
167
+ if (usage === undefined)
168
+ continue;
169
+ input += finiteNumber(usage.input);
170
+ output += finiteNumber(usage.output);
171
+ cacheRead += finiteNumber(usage.cacheRead);
172
+ cacheWrite += finiteNumber(usage.cacheWrite);
173
+ cost += isRecord(usage.cost) ? finiteNumber(usage.cost.total) : 0;
174
+ if (message?.role === "assistant") {
175
+ latestPrompt = {
176
+ input: finiteNumber(usage.input),
177
+ cacheRead: finiteNumber(usage.cacheRead),
178
+ cacheWrite: finiteNumber(usage.cacheWrite),
179
+ };
180
+ const promptTokens = latestPrompt.input + latestPrompt.cacheRead + latestPrompt.cacheWrite;
181
+ latestCacheHitRate = promptTokens > 0 ? (latestPrompt.cacheRead / promptTokens) * 100 : null;
182
+ }
183
+ }
184
+ const context = session?.getContextUsage?.();
185
+ const providerId = activeModel?.providerId;
186
+ const usingSubscription = providerId === "kimi-coding"
187
+ || (providerId !== undefined && runtime?.services.modelRuntime.isUsingSubscription?.(providerId) === true);
188
+ return {
189
+ input,
190
+ output,
191
+ cacheRead,
192
+ cacheWrite,
193
+ cost,
194
+ latestCacheHitRate,
195
+ latestPrompt,
196
+ contextAvailable: context !== undefined,
197
+ contextTokens: context?.tokens ?? null,
198
+ contextWindow: context?.contextWindow ?? 0,
199
+ contextPercent: context?.percent ?? null,
200
+ usingSubscription,
201
+ autoCompactEnabled: runtime?.services.settingsManager?.getCompactionEnabled?.() ?? true,
202
+ };
203
+ }
204
+ /** The owned UI session view model, copied so a published view never aliases adapter state. */
205
+ export function projectSessionView({ sessionId, state, engine, transcript, activeCommandIds }) {
206
+ const routed = engine.session?.routedModel;
207
+ const routedModel = routed === undefined ? null : readModel(routed.model);
208
+ return {
209
+ contractVersion: 1,
210
+ sessionId,
211
+ revision: state.revision,
212
+ lifecycle: state.lifecycle,
213
+ transcript,
214
+ editor: { ...state.editor, queuedSubmissions: [...state.editor.queuedSubmissions] },
215
+ status: {
216
+ ...state.status,
217
+ diagnostics: [...state.status.diagnostics],
218
+ badges: [...state.status.badges],
219
+ usage: state.usage(() => readUsageView(engine.session, engine.runtime, state.activeModel)),
220
+ footer: {
221
+ branch: engine.repository.branch,
222
+ repositoryPath: engine.repository.cwd,
223
+ pullRequest: engine.repository.pullRequest,
224
+ sessionName: engine.session?.sessionManager?.getSessionName() ?? null,
225
+ availableProviderCount: new Set(engine.runtime?.services.modelRuntime.getAvailableSnapshot?.().map(model => model.provider).filter(provider => provider !== undefined) ?? []).size,
226
+ extensionStatuses: [],
227
+ },
228
+ },
229
+ terminal: { ...state.terminal },
230
+ activeModel: state.activeModel === null ? null : { ...state.activeModel },
231
+ routedModel: routedModel === null ? null : {
232
+ model: routedModel,
233
+ ...(routed?.thinkingLevel === undefined ? {} : { thinkingLevel: readThinkingLevel(routed.thinkingLevel) }),
234
+ },
235
+ thinkingLevel: state.thinkingLevel,
236
+ activeCommandIds: [...activeCommandIds],
237
+ dialog: null,
238
+ overlay: null,
239
+ customizations: [],
240
+ diagnostics: state.diagnostics.map(diagnostic => ({ ...diagnostic })),
241
+ };
242
+ }
@@ -52,7 +52,6 @@ export declare class PiWorkflowRunner {
52
52
  bindClipboardWriter(writer: (text: string) => Promise<boolean>): () => void;
53
53
  /** Workflow and tree copying share the active owner's write fence and delivery acknowledgment. */
54
54
  copyWorkflowText(text: string): Promise<boolean>;
55
- abortBashWorkflow(): void;
56
55
  executeBashWorkflow(command: string, excludeFromContext: boolean): Promise<PiBashWorkflowResult>;
57
56
  reloadBlockedResult(): PiWorkflowResult | null;
58
57
  executeWorkflow(request: PiWorkflowRequest): Promise<PiWorkflowResult>;