@otto-code/client 0.6.7 → 0.7.1

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.
@@ -10,6 +10,28 @@ interface RuntimeMetricsContext {
10
10
  interface RuntimeMetricsOptions {
11
11
  windowMs?: number;
12
12
  }
13
+ /**
14
+ * Session totals, never pruned. The rolling buckets answer "what is happening
15
+ * now"; these answer "what has this connection cost the JS thread since the app
16
+ * started", which is the number you need when the complaint is that the app gets
17
+ * slower the longer it stays open.
18
+ */
19
+ export interface DaemonClientTrafficTotals {
20
+ messages: number;
21
+ bytes: number;
22
+ /** Main-thread ms spent inside inbound message handlers. */
23
+ handlerMs: number;
24
+ binaryFrames: number;
25
+ /** Distinct inbound message types seen. */
26
+ types: number;
27
+ }
28
+ export interface DaemonClientTrafficHotspot {
29
+ type: string;
30
+ count: number;
31
+ totalMs: number;
32
+ maxMs: number;
33
+ bytes: number;
34
+ }
13
35
  export declare class DaemonClientRuntimeMetrics {
14
36
  private readonly logger;
15
37
  private readonly context;
@@ -22,8 +44,17 @@ export declare class DaemonClientRuntimeMetrics {
22
44
  private readonly inboundAgentStreamCounts;
23
45
  private readonly inboundAgentStreamByAgentCounts;
24
46
  private readonly inboundBinaryFrameCounts;
47
+ private totalMessages;
48
+ private totalBytes;
49
+ private totalHandlerMs;
50
+ private totalBinaryFrames;
51
+ private readonly cumulativeByType;
25
52
  constructor(logger: RuntimeMetricsLogger, context: RuntimeMetricsContext, options?: RuntimeMetricsOptions);
26
53
  recordMessage(type: string, bytes: number, handlerMs: number): void;
54
+ getTrafficTotals(): DaemonClientTrafficTotals;
55
+ /** Inbound message types ranked by the main-thread time they have cost. */
56
+ getTrafficHotspots(limit?: number): DaemonClientTrafficHotspot[];
57
+ private recordCumulative;
27
58
  recordAgentStream(payload: Extract<SessionOutboundMessage, {
28
59
  type: "agent_stream";
29
60
  }>["payload"]): void;
@@ -11,6 +11,12 @@ export class DaemonClientRuntimeMetrics {
11
11
  this.inboundAgentStreamCounts = new Map();
12
12
  this.inboundAgentStreamByAgentCounts = new Map();
13
13
  this.inboundBinaryFrameCounts = new Map();
14
+ // Cumulative, deliberately outside the bucket pruning above.
15
+ this.totalMessages = 0;
16
+ this.totalBytes = 0;
17
+ this.totalHandlerMs = 0;
18
+ this.totalBinaryFrames = 0;
19
+ this.cumulativeByType = new Map();
14
20
  this.windowMs =
15
21
  typeof options?.windowMs === "number" && options.windowMs > 0
16
22
  ? options.windowMs
@@ -20,6 +26,55 @@ export class DaemonClientRuntimeMetrics {
20
26
  incrementCount(this.inboundMessageCounts, type, 1);
21
27
  incrementCount(this.inboundMessageBytes, type, bytes);
22
28
  incrementHandlerTiming(this.inboundMessageHandlerMs, type, handlerMs);
29
+ this.totalMessages += 1;
30
+ this.recordCumulative(type, bytes, handlerMs);
31
+ }
32
+ getTrafficTotals() {
33
+ return {
34
+ messages: this.totalMessages,
35
+ bytes: this.totalBytes,
36
+ handlerMs: this.totalHandlerMs,
37
+ binaryFrames: this.totalBinaryFrames,
38
+ types: this.cumulativeByType.size,
39
+ };
40
+ }
41
+ /** Inbound message types ranked by the main-thread time they have cost. */
42
+ getTrafficHotspots(limit = 15) {
43
+ // Copied out so a caller cannot mutate the running totals.
44
+ const rows = [];
45
+ for (const row of this.cumulativeByType.values()) {
46
+ rows.push({
47
+ type: row.type,
48
+ count: row.count,
49
+ totalMs: row.totalMs,
50
+ maxMs: row.maxMs,
51
+ bytes: row.bytes,
52
+ });
53
+ }
54
+ rows.sort((left, right) => right.totalMs - left.totalMs);
55
+ return rows.slice(0, limit);
56
+ }
57
+ // Shared by JSON messages and binary frames; the per-sink totals
58
+ // (`totalMessages` / `totalBinaryFrames`) are bumped by the callers so a
59
+ // binary frame is never counted as both.
60
+ recordCumulative(type, bytes, handlerMs) {
61
+ this.totalBytes += bytes;
62
+ this.totalHandlerMs += handlerMs;
63
+ const existing = this.cumulativeByType.get(type);
64
+ if (existing) {
65
+ existing.count += 1;
66
+ existing.bytes += bytes;
67
+ existing.totalMs += handlerMs;
68
+ existing.maxMs = Math.max(existing.maxMs, handlerMs);
69
+ return;
70
+ }
71
+ this.cumulativeByType.set(type, {
72
+ type,
73
+ count: 1,
74
+ bytes,
75
+ totalMs: handlerMs,
76
+ maxMs: handlerMs,
77
+ });
23
78
  }
24
79
  recordAgentStream(payload) {
25
80
  const { agentId, event } = payload;
@@ -31,6 +86,8 @@ export class DaemonClientRuntimeMetrics {
31
86
  incrementCount(this.inboundBinaryFrameCounts, kind, 1);
32
87
  incrementCount(this.inboundMessageBytes, `binary:${kind}`, bytes);
33
88
  incrementHandlerTiming(this.inboundMessageHandlerMs, `binary:${kind}`, handlerMs);
89
+ this.totalBinaryFrames += 1;
90
+ this.recordCumulative(`binary:${kind}`, bytes, handlerMs);
34
91
  }
35
92
  flush(options) {
36
93
  const now = Date.now();
@@ -1,11 +1,12 @@
1
1
  import type { z } from "zod";
2
2
  import { type ClientCapability } from "@otto-code/protocol/client-capabilities";
3
3
  import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ServerInfoStatusPayload } from "@otto-code/protocol/messages";
4
- import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreateOttoWorktreeRequest, CodeListFilesResponse, CodeSymbolLocation, FileDownloadTokenResponse, FileEol, FileReplaceFileResult, FileReplaceRequest, FileReplaceResponse, FileSearchResultPayload, FileSearchSummary, FileUploadResponse, FileExplorerResponse, FileWatchEventPayload, FileWriteResult, FetchAgentTimelineResponseMessage, AgentForkContextResponseMessage, GitSetupOptions, CheckoutStatusResponse, CheckoutCommitResponse, CheckoutGitCommitResponse, CheckoutGitCommitAgentResponse, CheckoutGitRollbackResponse, CheckoutGitFileHistoryResponse, CheckoutGitFileCommitDiffResponse, CheckoutGitFileBlameResponse, CheckoutGitFileOriginResponse, CheckoutGitGetOperationLogResponse, CheckoutMergeResponse, CheckoutMergeFromBaseResponse, CheckoutPullResponse, CheckoutPushResponse, CheckoutRefreshResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutGithubSetAutoMergeResponse, CheckoutGithubGetCheckDetailsResponse, PreviewListConfigResponse, PreviewStartResponse, PreviewBindTabResponse, PreviewStopResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, ValidateBranchResponse, BranchSuggestionsResponse, GitHubSearchResponse, GitHubSearchRequest, GitHostingProviderId, HostingSearchRequest, HostingSearchResponse, HostingAuthStatusResponse, DirectorySuggestionsResponse, OttoWorktreeListResponse, OttoWorktreeArchiveResponse, ProjectIconResponse, ContextReportGetResponseMessage, ContextEdgeConvertResponseMessage, ProjectAddResponse, OpenProjectResponseMessage, ArchiveWorkspaceResponseMessage, WorkspaceArchivePreflightResponse, WorktreeReattachListResponse, WorktreeReattachResponse, WorktreeReattachTarget, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, StatsActivityGetResponseMessage, StatsActivityResetResponseMessage, UsageLogGetResponseMessage, AgentContextGetUsageResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, TasksSuggestedStartMode, OttoConfigRaw, OttoConfigRevision, WorkspaceCreateRequest } from "@otto-code/protocol/messages";
4
+ import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreateOttoWorktreeRequest, CodeListFilesResponse, CodeSymbolLocation, CodeDefinitionLocation, CodeDefinitionStatus, CodeRenameApplyStatus, CodeRenameFileOutcome, CodeRenameUndoFile, CodeRenameUndoStatus, CodeHoverRange, CodeRenameEdit, CodeRenameFilePlan, LspLanguageState, LspRunningServer, CodeSolutionGetTreeResponse, CodeSolutionLoadProjectResponse, SolutionFormat, SolutionRef, SolutionTreeFolder, SolutionTreeProject, SolutionProjectNode, SolutionProjectStatus, SolutionPackageReference, FileCreateResult, FileDeleteResult, FileDownloadTokenResponse, FileEntryKind, FileEol, FileRenameResult, FileReplaceFileResult, FileReplaceRequest, FileReplaceResponse, FileSearchResultPayload, FileSearchSummary, FileUploadResponse, FileExplorerResponse, FileWatchEventPayload, FileWriteResult, FileRefineResult, FileRefineDocument, FileRefineReference, FetchAgentTimelineResponseMessage, AgentForkContextResponseMessage, GitSetupOptions, CheckoutStatusResponse, CheckoutCommitResponse, CheckoutGitCommitResponse, CheckoutGitCommitAgentResponse, CheckoutGitRollbackResponse, CheckoutGitFileHistoryResponse, CheckoutGitFileCommitDiffResponse, CheckoutGitFileBlameResponse, CheckoutGitFileOriginResponse, CheckoutGitGetOperationLogResponse, CheckoutMergeResponse, CheckoutMergeFromBaseResponse, CheckoutPullResponse, CheckoutPushResponse, CheckoutRefreshResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutGithubSetAutoMergeResponse, CheckoutGithubGetCheckDetailsResponse, PreviewListConfigResponse, PreviewStartResponse, PreviewBindTabResponse, PreviewStopResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, ValidateBranchResponse, BranchSuggestionsResponse, GitHubSearchResponse, GitHubSearchRequest, GitHostingProviderId, HostingSearchRequest, HostingSearchResponse, HostingAuthStatusResponse, DirectorySuggestionsResponse, OttoWorktreeListResponse, OttoWorktreeArchiveResponse, ProjectIconResponse, ContextCategory, ContextPromptPreviewGetResponseMessage, ContextReportGetResponseMessage, ContextEdgeConvertResponseMessage, ContextFindingsFixResponseMessage, PersonalityMemoryListResponseMessage, PersonalityMemoryUpdateResponseMessage, PersonalityMemoryTransferResponseMessage, PersonalityMemoryStatsResponseMessage, ProjectAddResponse, ProjectScaffoldGit, ProjectScaffoldProgress, ProjectScaffoldResponse, HostingListRepositoriesResponse, HostingListOwnersResponse, OpenProjectResponseMessage, ArchiveWorkspaceResponseMessage, WorkspaceArchivePreflightResponse, WorktreeBaseRefSetResponse, WorktreeReattachListResponse, WorktreeReattachResponse, WorktreeReattachTarget, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, StatsActivityGetResponseMessage, StatsActivityResetResponseMessage, UsageLogGetResponseMessage, AgentContextGetUsageResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, AttachmentsImagesClearResponse, AttachmentsImagesStatsResponse, HistoryAgentsClearArchivedResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, AgentPromptDelivery, TasksSuggestedStartMode, OttoConfigRaw, OttoConfigRevision, WorkspaceCreateRequest } from "@otto-code/protocol/messages";
5
5
  import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@otto-code/protocol/agent-types";
6
- import type { OrchestrationGraph, Run } from "@otto-code/protocol/orchestration";
6
+ import type { OrchestrationGraph, PromptTemplate, Run } from "@otto-code/protocol/orchestration";
7
7
  import type { CueMoment, MutableDaemonConfig, MutableDaemonConfigPatch, ProjectLink, SpeechSettingsOptions, SpeechTtsPreviewResult, SpeechTtsSpeakResult, SpeechTtsSpeakCancelResult, VisualizerVoiceCuesResult } from "@otto-code/protocol/messages";
8
8
  import { type DaemonTransportFactory, type WebSocketFactory } from "./daemon-client-transport.js";
9
+ import { type DaemonClientTrafficHotspot, type DaemonClientTrafficTotals } from "./daemon-client-runtime-metrics.js";
9
10
  import { type TerminalStreamEvent } from "./terminal-stream-router.js";
10
11
  import type { BrowserAutomationExecuteRequest, BrowserAutomationExecuteResponse } from "@otto-code/protocol/browser-automation/rpc-schemas";
11
12
  export interface Logger {
@@ -16,6 +17,12 @@ export interface Logger {
16
17
  }
17
18
  interface ImportAgentInputBase {
18
19
  cwd?: string;
20
+ /**
21
+ * Workspace the import was requested from. Supply it whenever the caller has
22
+ * one, so the imported session lands in that workspace instead of the daemon
23
+ * resolving (or minting) another workspace for the same directory.
24
+ */
25
+ workspaceId?: string;
19
26
  labels?: Record<string, string>;
20
27
  }
21
28
  export type ImportAgentInput = (ImportAgentInputBase & {
@@ -127,6 +134,18 @@ export interface SendMessageOptions {
127
134
  mimeType: string;
128
135
  }>;
129
136
  attachments?: SendAgentMessageRequest["attachments"];
137
+ /**
138
+ * How to reach the agent if it is busy. Omit for `interrupt` (cancel the
139
+ * in-flight turn and run this now). `queue` parks the message and runs it as
140
+ * the agent's next turn. Requires `server_info.features.steerQueue`.
141
+ */
142
+ delivery?: AgentPromptDelivery;
143
+ }
144
+ export interface SendAgentMessageResult {
145
+ /** True when the daemon parked the message instead of dispatching it. */
146
+ queued: boolean;
147
+ /** The queue entry's id, for finding this message again in `queuedMessages`. */
148
+ queuedMessageId: string | null;
130
149
  }
131
150
  type AgentConfigOverrides = Partial<Omit<AgentSessionConfig, "provider" | "cwd">>;
132
151
  export interface CreateAgentRequestOptions extends AgentConfigOverrides {
@@ -235,7 +254,48 @@ export interface FileWriteOptions {
235
254
  eol?: FileEol;
236
255
  requestId?: string;
237
256
  }
238
- export type { FileReplaceFileResult, FileSearchResultPayload, FileSearchSummary, FileWatchEventPayload, FileWriteResult, };
257
+ /**
258
+ * The general file-mutation surface — what exists in a directory, rather than
259
+ * what is inside a file. Gated on `features.fileMutations`; there is no
260
+ * client-side substitute, so callers check the flag before offering the action.
261
+ */
262
+ export interface FileCreateOptions {
263
+ cwd: string;
264
+ /** Workspace-relative. The parent directory must already exist. */
265
+ path: string;
266
+ kind: FileEntryKind;
267
+ requestId?: string;
268
+ }
269
+ export interface FileDeleteOptions {
270
+ cwd: string;
271
+ path: string;
272
+ /** Required for a directory with children; otherwise the daemon reports `not_empty`. */
273
+ recursive?: boolean;
274
+ requestId?: string;
275
+ }
276
+ export interface FileRenameOptions {
277
+ cwd: string;
278
+ path: string;
279
+ /** Workspace-relative destination. A different parent makes this a move. */
280
+ newPath: string;
281
+ requestId?: string;
282
+ }
283
+ /**
284
+ * Refine — ask the daemon for proposed rewrites of a pinned set of documents.
285
+ * This call never writes: accepted proposals go back through {@link
286
+ * DaemonClient.writeFile}, one per file, like any other save.
287
+ */
288
+ export interface FileRefineOptions {
289
+ /** Provider resolution only; the documents themselves travel inline. */
290
+ cwd: string;
291
+ /** What the model may rewrite — this list is the request's blast radius. */
292
+ documents: FileRefineDocument[];
293
+ /** What it may read for context but must never rewrite. */
294
+ references?: FileRefineReference[];
295
+ instruction: string;
296
+ requestId?: string;
297
+ }
298
+ export type { FileReplaceFileResult, FileSearchResultPayload, FileSearchSummary, FileWatchEventPayload, FileWriteResult, FileRefineResult, FileRefineDocument, FileRefineReference, };
239
299
  export interface FileSearchOptions {
240
300
  cwd: string;
241
301
  query: string;
@@ -250,8 +310,78 @@ export interface FileSearchOptions {
250
310
  }
251
311
  export type FileReplaceFilesInput = FileReplaceRequest["files"];
252
312
  export type FileReplaceResultPayload = FileReplaceResponse["payload"];
253
- export type { CodeSymbolLocation };
313
+ export type { CodeSymbolLocation, CodeHoverRange, CodeRenameEdit, CodeRenameFilePlan, LspLanguageState, LspRunningServer, };
254
314
  export type CodeListFilesResultPayload = CodeListFilesResponse["payload"];
315
+ /** 1-based, matching the wire and `CodeSymbolLocation`. */
316
+ export interface CodeDefinitionQuery {
317
+ cwd: string;
318
+ path: string;
319
+ line: number;
320
+ column: number;
321
+ }
322
+ export interface LspServersSnapshot {
323
+ languages: LspLanguageState[];
324
+ running: LspRunningServer[];
325
+ }
326
+ /** The Solution view (projects/solution-view). Independent of the LSP family above. */
327
+ export type { SolutionFormat, SolutionRef, SolutionTreeFolder, SolutionTreeProject, SolutionProjectNode, SolutionProjectStatus, SolutionPackageReference, };
328
+ export type SolutionTree = Omit<CodeSolutionGetTreeResponse["payload"], "cwd" | "error" | "requestId">;
329
+ export type SolutionProjectContents = Omit<CodeSolutionLoadProjectResponse["payload"], "cwd" | "solutionPath" | "requestId">;
330
+ export interface CodeHoverResult {
331
+ status: CodeDefinitionStatus;
332
+ /** Markdown, or null when the server had nothing to say here. */
333
+ markdown: string | null;
334
+ range: CodeHoverRange | null;
335
+ serverId: string | null;
336
+ error: string | null;
337
+ }
338
+ export interface CodeReferencesResult {
339
+ status: CodeDefinitionStatus;
340
+ locations: CodeDefinitionLocation[];
341
+ error: string | null;
342
+ }
343
+ export interface CodeRenamePreviewQuery extends CodeDefinitionQuery {
344
+ newName: string;
345
+ }
346
+ export interface CodeRenamePlan {
347
+ status: CodeDefinitionStatus;
348
+ files: CodeRenameFilePlan[];
349
+ /** Blast radius, so a dry-run surface can lead with it. */
350
+ fileCount: number;
351
+ editCount: number;
352
+ /** Identity of this exact plan. Send it back to apply; see `applyCodeRename`. */
353
+ planId: string;
354
+ error: string | null;
355
+ }
356
+ export interface CodeRenameApplyQuery extends CodeRenamePreviewQuery {
357
+ /** The `planId` of the plan that was actually shown to the user. */
358
+ planId: string;
359
+ }
360
+ export interface CodeRenameApplyOutcome {
361
+ status: CodeRenameApplyStatus;
362
+ /** Identity of the run, for undo. Null when nothing ran. */
363
+ runId: string | null;
364
+ files: CodeRenameFileOutcome[];
365
+ appliedFiles: number;
366
+ appliedEdits: number;
367
+ skippedEdits: number;
368
+ /** True only when every planned edit landed. */
369
+ complete: boolean;
370
+ error: string | null;
371
+ }
372
+ export interface CodeRenameUndoOutcome {
373
+ status: CodeRenameUndoStatus;
374
+ files: CodeRenameUndoFile[];
375
+ restoredFiles: number;
376
+ /** True only when every file the run wrote was put back. */
377
+ complete: boolean;
378
+ error: string | null;
379
+ }
380
+ export interface CodeDefinitionResult {
381
+ status: CodeDefinitionStatus;
382
+ locations: CodeDefinitionLocation[];
383
+ error: string | null;
384
+ }
255
385
  export interface FileUploadInput {
256
386
  fileName: string;
257
387
  mimeType: string;
@@ -630,8 +760,12 @@ export interface RenameTerminalInput {
630
760
  }
631
761
  type OpenProjectPayload = OpenProjectResponseMessage["payload"];
632
762
  type ProjectAddPayload = ProjectAddResponse["payload"];
763
+ export type ProjectScaffoldPayload = ProjectScaffoldResponse["payload"];
764
+ export type HostingListRepositoriesPayload = HostingListRepositoriesResponse["payload"];
765
+ export type HostingListOwnersPayload = HostingListOwnersResponse["payload"];
633
766
  type ArchiveWorkspacePayload = ArchiveWorkspaceResponseMessage["payload"];
634
767
  type WorkspaceArchivePreflightPayload = WorkspaceArchivePreflightResponse["payload"];
768
+ type WorktreeBaseRefSetPayload = WorktreeBaseRefSetResponse["payload"];
635
769
  type WorktreeReattachListPayload = WorktreeReattachListResponse["payload"];
636
770
  type WorktreeReattachPayload = WorktreeReattachResponse["payload"];
637
771
  type WorkspaceSetupStatusPayload = WorkspaceSetupStatusResponseMessage["payload"];
@@ -668,6 +802,7 @@ export declare class DaemonClient {
668
802
  private checkoutDiffSubscriptions;
669
803
  private terminalDirectorySubscriptions;
670
804
  private readonly terminalStreams;
805
+ private readonly scaffoldProgressListeners;
671
806
  private pendingBinaryFileReads;
672
807
  private activeBinaryFileTransfers;
673
808
  private completedBinaryFileReads;
@@ -766,6 +901,20 @@ export declare class DaemonClient {
766
901
  fetchWorkspaces(options?: FetchWorkspacesOptions): Promise<FetchWorkspacesPayload>;
767
902
  openProject(cwd: string, requestId?: string): Promise<OpenProjectPayload>;
768
903
  addProject(cwd: string, requestId?: string): Promise<ProjectAddPayload>;
904
+ scaffoldProject(options: {
905
+ parentDirectory: string;
906
+ folderName?: string;
907
+ git: ProjectScaffoldGit;
908
+ onProgress?: (payload: ProjectScaffoldProgress["payload"]) => void;
909
+ }, requestId?: string): Promise<ProjectScaffoldPayload>;
910
+ listHostingRepositories(options: {
911
+ provider: GitHostingProviderId;
912
+ query?: string;
913
+ limit?: number;
914
+ }, requestId?: string): Promise<HostingListRepositoriesPayload>;
915
+ listHostingOwners(options: {
916
+ provider: GitHostingProviderId;
917
+ }, requestId?: string): Promise<HostingListOwnersPayload>;
769
918
  startWorkspaceScript(workspaceId: string, scriptName: string, requestId?: string): Promise<Extract<SessionOutboundMessage, {
770
919
  type: "start_workspace_script_response";
771
920
  }>["payload"]>;
@@ -774,6 +923,7 @@ export declare class DaemonClient {
774
923
  requestId?: string;
775
924
  }): Promise<ArchiveWorkspacePayload>;
776
925
  workspaceArchivePreflight(workspaceId: string, requestId?: string): Promise<WorkspaceArchivePreflightPayload>;
926
+ setWorktreeBaseRef(workspaceId: string, baseRef: string | null, requestId?: string): Promise<WorktreeBaseRefSetPayload>;
777
927
  listReattachableWorktrees(scope: {
778
928
  projectId?: string;
779
929
  cwd?: string;
@@ -788,6 +938,42 @@ export declare class DaemonClient {
788
938
  private resubscribeFileWatches;
789
939
  createAgent(options: CreateAgentRequestOptions): Promise<AgentSnapshotPayload>;
790
940
  deleteAgent(agentId: string): Promise<void>;
941
+ /**
942
+ * Bulk-delete archived chat records on this host. Server-side by necessity:
943
+ * the client's history list is cursor-paginated across hosts and never holds
944
+ * the whole archived set. Pass `dryRun: true` first to get the count the
945
+ * confirm dialog quotes, then the same call with `dryRun: false` to delete.
946
+ *
947
+ * Removes Otto's records only — provider transcripts are left on disk. Gated
948
+ * by `server_info.features.historyDelete`; there is no fallback path, so check
949
+ * the flag before offering the action.
950
+ */
951
+ clearArchivedAgents(options: {
952
+ dryRun: boolean;
953
+ olderThanDays?: number;
954
+ requestId?: string;
955
+ }): Promise<HistoryAgentsClearArchivedResponse["payload"]>;
956
+ /**
957
+ * How much disk the images agents produced occupy on this host, plus the
958
+ * retention policy currently ageing them out. Gated by
959
+ * `server_info.features.attachmentStorage`.
960
+ */
961
+ getAttachmentImageStats(requestId?: string): Promise<AttachmentsImagesStatsResponse["payload"]>;
962
+ /**
963
+ * Reclaims the materialized image store. Call once with `dryRun: true` for
964
+ * the count and size the confirm dialog quotes, then again with
965
+ * `dryRun: false` to delete.
966
+ *
967
+ * Cleared images do not come back: a message that referenced one renders its
968
+ * alt text from then on. Scope is the whole host — filenames are a content
969
+ * hash, so per-chat or per-workspace scope does not exist. Gated by
970
+ * `server_info.features.attachmentStorage`.
971
+ */
972
+ clearAttachmentImages(options: {
973
+ dryRun: boolean;
974
+ olderThanDays?: number;
975
+ requestId?: string;
976
+ }): Promise<AttachmentsImagesClearResponse["payload"]>;
791
977
  archiveAgent(agentId: string): Promise<{
792
978
  archivedAt: string;
793
979
  }>;
@@ -835,12 +1021,23 @@ export declare class DaemonClient {
835
1021
  cancelRun(runId: string): Promise<boolean>;
836
1022
  /** Orchestration: delete every finished (done/failed/canceled) run. */
837
1023
  clearFinishedRuns(): Promise<string[]>;
1024
+ /**
1025
+ * Orchestration: delete one finished (or draft) run. Throws with the
1026
+ * daemon's reason when it refuses — an active run has to be canceled first.
1027
+ */
1028
+ deleteRun(runId: string): Promise<string>;
838
1029
  /** Orchestration: list the host's reusable graph templates. */
839
1030
  listOrchestrationGraphs(): Promise<OrchestrationGraph[]>;
840
1031
  /** Orchestration: upsert a graph template. Returns the persisted graph. */
841
1032
  saveOrchestrationGraph(graph: OrchestrationGraph): Promise<OrchestrationGraph>;
842
1033
  /** Orchestration: delete a graph template (built-in starters refuse). */
843
1034
  deleteOrchestrationGraph(graphId: string): Promise<boolean>;
1035
+ /** Orchestration: list the host's reusable prompt templates and snippets. */
1036
+ listPromptTemplates(): Promise<PromptTemplate[]>;
1037
+ /** Orchestration: upsert a prompt template. Returns the persisted template. */
1038
+ savePromptTemplate(template: PromptTemplate): Promise<PromptTemplate>;
1039
+ /** Orchestration: delete a prompt template (built-in starters refuse). */
1040
+ deletePromptTemplate(templateId: string): Promise<boolean>;
844
1041
  /**
845
1042
  * Orchestration: start (or draft) a user-initiated orchestration. Returns the
846
1043
  * run id (graph flavor) and the orchestrator chat's agent id to navigate to.
@@ -886,8 +1083,26 @@ export declare class DaemonClient {
886
1083
  refreshAgent(agentId: string, requestId?: string): Promise<AgentRefreshedStatusPayload>;
887
1084
  fetchAgentTimeline(agentId: string, options?: FetchAgentTimelineOptions): Promise<FetchAgentTimelinePayload>;
888
1085
  buildAgentForkContext(agentId: string, options?: AgentForkContextOptions): Promise<AgentForkContextPayload>;
889
- sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
1086
+ sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<SendAgentMessageResult>;
890
1087
  sendMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
1088
+ /**
1089
+ * Pull one message back out of an agent's queue. Returns its text so the
1090
+ * caller can put it back in the composer, or null when the turn already
1091
+ * drained it. Requires `server_info.features.steerQueue`.
1092
+ */
1093
+ removeQueuedAgentMessage(agentId: string, messageId: string): Promise<{
1094
+ id: string;
1095
+ text: string;
1096
+ } | null>;
1097
+ /**
1098
+ * Move one queued message to a new position. Resolves false when the entry
1099
+ * was already drained or was already there — the authoritative order arrives
1100
+ * on the agent snapshot either way. Requires
1101
+ * `server_info.features.steerQueueReorder`.
1102
+ */
1103
+ reorderQueuedAgentMessage(agentId: string, messageId: string, toIndex: number): Promise<boolean>;
1104
+ /** Drop every message queued behind an agent's current turn. */
1105
+ clearAgentQueue(agentId: string): Promise<number>;
891
1106
  rewindAgent(agentId: string, messageId: string, mode: "conversation" | "files" | "both"): Promise<AgentRewindResponseMessage["payload"]>;
892
1107
  cancelAgent(agentId: string): Promise<{
893
1108
  cancelled?: boolean;
@@ -1084,6 +1299,13 @@ export declare class DaemonClient {
1084
1299
  readTextFile(cwd: string, path: string, requestId?: string): Promise<TextFileReadResult>;
1085
1300
  /** Conditional save — see FileWriteRequestSchema for the no-clobber contract. */
1086
1301
  writeFile(options: FileWriteOptions): Promise<FileWriteResult>;
1302
+ /** Create an empty file or a directory. Never overwrites — see FileCreateResultSchema. */
1303
+ createFileEntry(options: FileCreateOptions): Promise<FileCreateResult>;
1304
+ /** Permanent delete — an unlink, not a move to any trash. */
1305
+ deleteFileEntry(options: FileDeleteOptions): Promise<FileDeleteResult>;
1306
+ /** Rename, which is also move. Never clobbers an occupied destination. */
1307
+ renameFileEntry(options: FileRenameOptions): Promise<FileRenameResult>;
1308
+ refineFile(options: FileRefineOptions): Promise<FileRefineResult>;
1087
1309
  /**
1088
1310
  * Project-wide search. Per-file results stream through onFileResult (the
1089
1311
  * daemon emits them in order, before the summary response resolves); the
@@ -1094,6 +1316,81 @@ export declare class DaemonClient {
1094
1316
  listCodeFiles(cwd: string, requestId?: string): Promise<CodeListFilesResultPayload>;
1095
1317
  /** Name-based go-to-definition: one hit jumps, multiple hits are a picker. */
1096
1318
  findCodeSymbols(cwd: string, name: string, requestId?: string): Promise<CodeSymbolLocation[]>;
1319
+ /**
1320
+ * Language-server-backed go-to-definition. Unlike `findCodeSymbols` this resolves the
1321
+ * reference *at a position*, so multiple results mean real overloads or
1322
+ * implementations rather than "two files happen to use this name".
1323
+ *
1324
+ * Line and column are 1-based. Returns the whole payload, not just the locations,
1325
+ * because `indexing` and `unavailable` are answers the caller must show differently
1326
+ * from an empty result.
1327
+ */
1328
+ findCodeDefinition(input: CodeDefinitionQuery, requestId?: string): Promise<CodeDefinitionResult>;
1329
+ /**
1330
+ * Mirror the editor's current buffer to the daemon so definitions resolve against
1331
+ * unsaved edits. Debounced by the caller — this is not a per-keystroke RPC.
1332
+ */
1333
+ syncCodeDocument(cwd: string, path: string, text: string, requestId?: string): Promise<void>;
1334
+ /** Release the daemon-side mirror when a file tab closes. */
1335
+ closeCodeDocument(cwd: string, path: string, requestId?: string): Promise<void>;
1336
+ /**
1337
+ * The language server's own explanation of the symbol at a position. Returns the
1338
+ * whole payload: `indexing` and `unavailable` read differently to a user than "the
1339
+ * server had nothing to say", which is `ok` with a null `markdown`.
1340
+ */
1341
+ getCodeHover(input: CodeDefinitionQuery, requestId?: string): Promise<CodeHoverResult>;
1342
+ /** Every reference to the symbol at a position, for the references results tab. */
1343
+ findCodeReferences(input: CodeDefinitionQuery, requestId?: string): Promise<CodeReferencesResult>;
1344
+ /**
1345
+ * A rename **dry run** — every edit it would make, and nothing written. The client
1346
+ * puts this in front of the user as a job to audit before applying.
1347
+ */
1348
+ previewCodeRename(input: CodeRenamePreviewQuery, requestId?: string): Promise<CodeRenamePlan>;
1349
+ /**
1350
+ * Execute a rename the user audited. Sends the and NOT the edits: the daemon
1351
+ * recomputes the plan and refuses unless the identity still matches, which is what keeps
1352
+ * this from being an arbitrary-write RPC and what makes "what you approved is what
1353
+ * happens" enforceable rather than merely intended.
1354
+ */
1355
+ applyCodeRename(input: CodeRenameApplyQuery, requestId?: string): Promise<CodeRenameApplyOutcome>;
1356
+ /**
1357
+ * Take a rename run back. Sends only the run id: the daemon holds the before-images, and
1358
+ * restores a file only if it still holds exactly what the run wrote.
1359
+ */
1360
+ undoCodeRename(cwd: string, runId: string, requestId?: string): Promise<CodeRenameUndoOutcome>;
1361
+ /**
1362
+ * Live language-server state for the Daemon → Code screen: what this host can
1363
+ * supply, and what is running now. `cwd` scopes availability, since a server can
1364
+ * be present in one workspace's `node_modules` and absent in another's.
1365
+ */
1366
+ listLspServers(cwd: string, requestId?: string): Promise<LspServersSnapshot>;
1367
+ /** Stop one running language server. */
1368
+ stopLspServer(rootPath: string, serverId: string, requestId?: string): Promise<void>;
1369
+ /**
1370
+ * Solutions in a workspace, which is what decides whether the Files tab shows a view switcher
1371
+ * at all.
1372
+ *
1373
+ * Never throws and never carries an error the caller has to render. A workspace with no
1374
+ * solution, a host with no .NET SDK, and a host with the feature switched off all answer with an
1375
+ * empty list, so the caller has one silent case — "no switcher" — rather than four states.
1376
+ */
1377
+ listSolutions(cwd: string, requestId?: string): Promise<SolutionRef[]>;
1378
+ /** One solution's organisation: folders, the projects inside them, configurations. */
1379
+ getSolutionTree(input: {
1380
+ cwd: string;
1381
+ solutionPath: string;
1382
+ }, requestId?: string): Promise<SolutionTree>;
1383
+ /**
1384
+ * One project's evaluated file membership, fetched on expand.
1385
+ *
1386
+ * A `failed` status is a normal answer, not an exception: the daemon carries MSBuild's own
1387
+ * message for a project it refused, and one bad project must not blank the tree.
1388
+ */
1389
+ loadSolutionProject(input: {
1390
+ cwd: string;
1391
+ solutionPath: string;
1392
+ projectPath: string;
1393
+ }, requestId?: string): Promise<SolutionProjectContents>;
1097
1394
  /** Definition symbols for a single file (document outline). */
1098
1395
  getCodeOutline(cwd: string, path: string, requestId?: string): Promise<CodeSymbolLocation[]>;
1099
1396
  /** Preview-first project replace — see FileReplaceRequestSchema. */
@@ -1121,7 +1418,59 @@ export declare class DaemonClient {
1121
1418
  workspaceId: string;
1122
1419
  provider?: string;
1123
1420
  windowTokens?: number;
1421
+ personalityId?: string;
1124
1422
  }, requestId?: string): Promise<ContextReportGetResponseMessage["payload"]>;
1423
+ /**
1424
+ * The assembled prompt, for reading. Takes the same what-if inputs as the
1425
+ * report so the text on screen always matches the numbers beside it.
1426
+ */
1427
+ requestContextPromptPreview(input: {
1428
+ workspaceId: string;
1429
+ provider?: string;
1430
+ windowTokens?: number;
1431
+ personalityId?: string;
1432
+ /** Assemble only this section; omitted means the whole prompt. */
1433
+ category?: ContextCategory;
1434
+ }, requestId?: string): Promise<ContextPromptPreviewGetResponseMessage["payload"]>;
1435
+ /**
1436
+ * A personality's accrued lessons plus the EXACT brief the daemon would inject
1437
+ * for `projectRoot`. The brief is returned rather than rebuilt client-side
1438
+ * because memory is only trustworthy if what you are shown is what is sent.
1439
+ */
1440
+ listPersonalityMemory(input: {
1441
+ personalityId: string;
1442
+ workspaceId?: string;
1443
+ projectRoot?: string;
1444
+ }, requestId?: string): Promise<PersonalityMemoryListResponseMessage["payload"]>;
1445
+ /**
1446
+ * Add (no `entryId`), edit, or forget (`drop`) one lesson.
1447
+ *
1448
+ * Pass `workspaceId` whenever the write may be project-scoped: the daemon
1449
+ * binds the entry to the repo root that workspace resolves to, and an entry
1450
+ * scoped to "project" with no root is filtered out of every brief — stored,
1451
+ * listed, and never sent.
1452
+ */
1453
+ updatePersonalityMemory(input: {
1454
+ personalityId: string;
1455
+ entryId?: string;
1456
+ text?: string;
1457
+ scope?: string;
1458
+ workspaceId?: string;
1459
+ projectRoot?: string;
1460
+ drop?: boolean;
1461
+ }, requestId?: string): Promise<PersonalityMemoryUpdateResponseMessage["payload"]>;
1462
+ /**
1463
+ * Resolve a deleted personality's lessons: move them to another personality or
1464
+ * discard them. Called BEFORE the roster write, so a failure leaves both the
1465
+ * personality and its memory intact.
1466
+ */
1467
+ transferPersonalityMemory(input: {
1468
+ fromPersonalityId: string;
1469
+ toPersonalityId?: string;
1470
+ mode: "transfer" | "delete";
1471
+ }, requestId?: string): Promise<PersonalityMemoryTransferResponseMessage["payload"]>;
1472
+ /** Per-personality lesson counts, for the accrual indicator and the selector. */
1473
+ getPersonalityMemoryStats(requestId?: string): Promise<PersonalityMemoryStatsResponseMessage["payload"]>;
1125
1474
  /** Rewrites one reference between "always loaded" and "link only". */
1126
1475
  requestContextEdgeConvert(input: {
1127
1476
  workspaceId: string;
@@ -1133,6 +1482,18 @@ export declare class DaemonClient {
1133
1482
  };
1134
1483
  target: "import" | "reference";
1135
1484
  }, requestId?: string): Promise<ContextEdgeConvertResponseMessage["payload"]>;
1485
+ /** Deletes every mechanically-fixable finding's range in one pass. */
1486
+ requestContextFindingsFix(input: {
1487
+ workspaceId: string;
1488
+ findings: Array<{
1489
+ filePath: string;
1490
+ range: {
1491
+ start: number;
1492
+ end: number;
1493
+ };
1494
+ snippet: string;
1495
+ }>;
1496
+ }, requestId?: string): Promise<ContextFindingsFixResponseMessage["payload"]>;
1136
1497
  listProviderModels(provider: AgentProvider, options?: {
1137
1498
  cwd?: string;
1138
1499
  requestId?: string;
@@ -1342,6 +1703,14 @@ export declare class DaemonClient {
1342
1703
  waitForTerminalStreamEvent(predicate: (event: TerminalStreamEvent) => boolean, timeout?: number): Promise<TerminalStreamEvent>;
1343
1704
  private createRequestId;
1344
1705
  getLastServerInfoMessage(): ServerInfoStatusPayload | null;
1706
+ /**
1707
+ * Session totals for inbound daemon traffic, including the main-thread time
1708
+ * spent handling it. Null when runtime metrics are disabled for this client.
1709
+ * Read by the app's resource monitor — the wire is a first-class suspect when
1710
+ * the UI thread degrades, so it has to be measurable rather than inferred.
1711
+ */
1712
+ getTrafficTotals(): DaemonClientTrafficTotals | null;
1713
+ getTrafficHotspots(limit?: number): DaemonClientTrafficHotspot[];
1345
1714
  private resolveTransportUrlForAttempt;
1346
1715
  private sendHelloMessage;
1347
1716
  private disposeTransport;