@otto-code/client 0.6.6 → 0.7.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.
@@ -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, 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, 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, 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 { Run } from "@otto-code/protocol/orchestration";
7
- import type { CueMoment, MutableDaemonConfig, MutableDaemonConfigPatch, ProjectLink, SpeechSettingsOptions, SpeechTtsPreviewResult, VisualizerVoiceCuesResult } from "@otto-code/protocol/messages";
6
+ import type { OrchestrationGraph, PromptTemplate, Run } from "@otto-code/protocol/orchestration";
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 {
@@ -127,6 +128,18 @@ export interface SendMessageOptions {
127
128
  mimeType: string;
128
129
  }>;
129
130
  attachments?: SendAgentMessageRequest["attachments"];
131
+ /**
132
+ * How to reach the agent if it is busy. Omit for `interrupt` (cancel the
133
+ * in-flight turn and run this now). `queue` parks the message and runs it as
134
+ * the agent's next turn. Requires `server_info.features.steerQueue`.
135
+ */
136
+ delivery?: AgentPromptDelivery;
137
+ }
138
+ export interface SendAgentMessageResult {
139
+ /** True when the daemon parked the message instead of dispatching it. */
140
+ queued: boolean;
141
+ /** The queue entry's id, for finding this message again in `queuedMessages`. */
142
+ queuedMessageId: string | null;
130
143
  }
131
144
  type AgentConfigOverrides = Partial<Omit<AgentSessionConfig, "provider" | "cwd">>;
132
145
  export interface CreateAgentRequestOptions extends AgentConfigOverrides {
@@ -235,7 +248,48 @@ export interface FileWriteOptions {
235
248
  eol?: FileEol;
236
249
  requestId?: string;
237
250
  }
238
- export type { FileReplaceFileResult, FileSearchResultPayload, FileSearchSummary, FileWatchEventPayload, FileWriteResult, };
251
+ /**
252
+ * The general file-mutation surface — what exists in a directory, rather than
253
+ * what is inside a file. Gated on `features.fileMutations`; there is no
254
+ * client-side substitute, so callers check the flag before offering the action.
255
+ */
256
+ export interface FileCreateOptions {
257
+ cwd: string;
258
+ /** Workspace-relative. The parent directory must already exist. */
259
+ path: string;
260
+ kind: FileEntryKind;
261
+ requestId?: string;
262
+ }
263
+ export interface FileDeleteOptions {
264
+ cwd: string;
265
+ path: string;
266
+ /** Required for a directory with children; otherwise the daemon reports `not_empty`. */
267
+ recursive?: boolean;
268
+ requestId?: string;
269
+ }
270
+ export interface FileRenameOptions {
271
+ cwd: string;
272
+ path: string;
273
+ /** Workspace-relative destination. A different parent makes this a move. */
274
+ newPath: string;
275
+ requestId?: string;
276
+ }
277
+ /**
278
+ * Refine — ask the daemon for proposed rewrites of a pinned set of documents.
279
+ * This call never writes: accepted proposals go back through {@link
280
+ * DaemonClient.writeFile}, one per file, like any other save.
281
+ */
282
+ export interface FileRefineOptions {
283
+ /** Provider resolution only; the documents themselves travel inline. */
284
+ cwd: string;
285
+ /** What the model may rewrite — this list is the request's blast radius. */
286
+ documents: FileRefineDocument[];
287
+ /** What it may read for context but must never rewrite. */
288
+ references?: FileRefineReference[];
289
+ instruction: string;
290
+ requestId?: string;
291
+ }
292
+ export type { FileReplaceFileResult, FileSearchResultPayload, FileSearchSummary, FileWatchEventPayload, FileWriteResult, FileRefineResult, FileRefineDocument, FileRefineReference, };
239
293
  export interface FileSearchOptions {
240
294
  cwd: string;
241
295
  query: string;
@@ -250,8 +304,78 @@ export interface FileSearchOptions {
250
304
  }
251
305
  export type FileReplaceFilesInput = FileReplaceRequest["files"];
252
306
  export type FileReplaceResultPayload = FileReplaceResponse["payload"];
253
- export type { CodeSymbolLocation };
307
+ export type { CodeSymbolLocation, CodeHoverRange, CodeRenameEdit, CodeRenameFilePlan, LspLanguageState, LspRunningServer, };
254
308
  export type CodeListFilesResultPayload = CodeListFilesResponse["payload"];
309
+ /** 1-based, matching the wire and `CodeSymbolLocation`. */
310
+ export interface CodeDefinitionQuery {
311
+ cwd: string;
312
+ path: string;
313
+ line: number;
314
+ column: number;
315
+ }
316
+ export interface LspServersSnapshot {
317
+ languages: LspLanguageState[];
318
+ running: LspRunningServer[];
319
+ }
320
+ /** The Solution view (projects/solution-view). Independent of the LSP family above. */
321
+ export type { SolutionFormat, SolutionRef, SolutionTreeFolder, SolutionTreeProject, SolutionProjectNode, SolutionProjectStatus, SolutionPackageReference, };
322
+ export type SolutionTree = Omit<CodeSolutionGetTreeResponse["payload"], "cwd" | "error" | "requestId">;
323
+ export type SolutionProjectContents = Omit<CodeSolutionLoadProjectResponse["payload"], "cwd" | "solutionPath" | "requestId">;
324
+ export interface CodeHoverResult {
325
+ status: CodeDefinitionStatus;
326
+ /** Markdown, or null when the server had nothing to say here. */
327
+ markdown: string | null;
328
+ range: CodeHoverRange | null;
329
+ serverId: string | null;
330
+ error: string | null;
331
+ }
332
+ export interface CodeReferencesResult {
333
+ status: CodeDefinitionStatus;
334
+ locations: CodeDefinitionLocation[];
335
+ error: string | null;
336
+ }
337
+ export interface CodeRenamePreviewQuery extends CodeDefinitionQuery {
338
+ newName: string;
339
+ }
340
+ export interface CodeRenamePlan {
341
+ status: CodeDefinitionStatus;
342
+ files: CodeRenameFilePlan[];
343
+ /** Blast radius, so a dry-run surface can lead with it. */
344
+ fileCount: number;
345
+ editCount: number;
346
+ /** Identity of this exact plan. Send it back to apply; see `applyCodeRename`. */
347
+ planId: string;
348
+ error: string | null;
349
+ }
350
+ export interface CodeRenameApplyQuery extends CodeRenamePreviewQuery {
351
+ /** The `planId` of the plan that was actually shown to the user. */
352
+ planId: string;
353
+ }
354
+ export interface CodeRenameApplyOutcome {
355
+ status: CodeRenameApplyStatus;
356
+ /** Identity of the run, for undo. Null when nothing ran. */
357
+ runId: string | null;
358
+ files: CodeRenameFileOutcome[];
359
+ appliedFiles: number;
360
+ appliedEdits: number;
361
+ skippedEdits: number;
362
+ /** True only when every planned edit landed. */
363
+ complete: boolean;
364
+ error: string | null;
365
+ }
366
+ export interface CodeRenameUndoOutcome {
367
+ status: CodeRenameUndoStatus;
368
+ files: CodeRenameUndoFile[];
369
+ restoredFiles: number;
370
+ /** True only when every file the run wrote was put back. */
371
+ complete: boolean;
372
+ error: string | null;
373
+ }
374
+ export interface CodeDefinitionResult {
375
+ status: CodeDefinitionStatus;
376
+ locations: CodeDefinitionLocation[];
377
+ error: string | null;
378
+ }
255
379
  export interface FileUploadInput {
256
380
  fileName: string;
257
381
  mimeType: string;
@@ -630,7 +754,14 @@ export interface RenameTerminalInput {
630
754
  }
631
755
  type OpenProjectPayload = OpenProjectResponseMessage["payload"];
632
756
  type ProjectAddPayload = ProjectAddResponse["payload"];
757
+ export type ProjectScaffoldPayload = ProjectScaffoldResponse["payload"];
758
+ export type HostingListRepositoriesPayload = HostingListRepositoriesResponse["payload"];
759
+ export type HostingListOwnersPayload = HostingListOwnersResponse["payload"];
633
760
  type ArchiveWorkspacePayload = ArchiveWorkspaceResponseMessage["payload"];
761
+ type WorkspaceArchivePreflightPayload = WorkspaceArchivePreflightResponse["payload"];
762
+ type WorktreeBaseRefSetPayload = WorktreeBaseRefSetResponse["payload"];
763
+ type WorktreeReattachListPayload = WorktreeReattachListResponse["payload"];
764
+ type WorktreeReattachPayload = WorktreeReattachResponse["payload"];
634
765
  type WorkspaceSetupStatusPayload = WorkspaceSetupStatusResponseMessage["payload"];
635
766
  export interface FetchAgentResult {
636
767
  agent: AgentSnapshotPayload;
@@ -665,6 +796,7 @@ export declare class DaemonClient {
665
796
  private checkoutDiffSubscriptions;
666
797
  private terminalDirectorySubscriptions;
667
798
  private readonly terminalStreams;
799
+ private readonly scaffoldProgressListeners;
668
800
  private pendingBinaryFileReads;
669
801
  private activeBinaryFileTransfers;
670
802
  private completedBinaryFileReads;
@@ -763,10 +895,34 @@ export declare class DaemonClient {
763
895
  fetchWorkspaces(options?: FetchWorkspacesOptions): Promise<FetchWorkspacesPayload>;
764
896
  openProject(cwd: string, requestId?: string): Promise<OpenProjectPayload>;
765
897
  addProject(cwd: string, requestId?: string): Promise<ProjectAddPayload>;
898
+ scaffoldProject(options: {
899
+ parentDirectory: string;
900
+ folderName?: string;
901
+ git: ProjectScaffoldGit;
902
+ onProgress?: (payload: ProjectScaffoldProgress["payload"]) => void;
903
+ }, requestId?: string): Promise<ProjectScaffoldPayload>;
904
+ listHostingRepositories(options: {
905
+ provider: GitHostingProviderId;
906
+ query?: string;
907
+ limit?: number;
908
+ }, requestId?: string): Promise<HostingListRepositoriesPayload>;
909
+ listHostingOwners(options: {
910
+ provider: GitHostingProviderId;
911
+ }, requestId?: string): Promise<HostingListOwnersPayload>;
766
912
  startWorkspaceScript(workspaceId: string, scriptName: string, requestId?: string): Promise<Extract<SessionOutboundMessage, {
767
913
  type: "start_workspace_script_response";
768
914
  }>["payload"]>;
769
- archiveWorkspace(workspaceId: string, requestId?: string): Promise<ArchiveWorkspacePayload>;
915
+ archiveWorkspace(workspaceId: string, options?: {
916
+ branchDisposition?: "keep" | "delete";
917
+ requestId?: string;
918
+ }): Promise<ArchiveWorkspacePayload>;
919
+ workspaceArchivePreflight(workspaceId: string, requestId?: string): Promise<WorkspaceArchivePreflightPayload>;
920
+ setWorktreeBaseRef(workspaceId: string, baseRef: string | null, requestId?: string): Promise<WorktreeBaseRefSetPayload>;
921
+ listReattachableWorktrees(scope: {
922
+ projectId?: string;
923
+ cwd?: string;
924
+ }, requestId?: string): Promise<WorktreeReattachListPayload>;
925
+ reattachWorktree(target: WorktreeReattachTarget, requestId?: string): Promise<WorktreeReattachPayload>;
770
926
  fetchWorkspaceSetupStatus(workspaceId: string, requestId?: string): Promise<WorkspaceSetupStatusPayload>;
771
927
  fetchAgent(options: FetchAgentOptions): Promise<FetchAgentResult | null>;
772
928
  fetchAgent(agentId: string, requestId?: string): Promise<FetchAgentResult | null>;
@@ -776,6 +932,21 @@ export declare class DaemonClient {
776
932
  private resubscribeFileWatches;
777
933
  createAgent(options: CreateAgentRequestOptions): Promise<AgentSnapshotPayload>;
778
934
  deleteAgent(agentId: string): Promise<void>;
935
+ /**
936
+ * Bulk-delete archived chat records on this host. Server-side by necessity:
937
+ * the client's history list is cursor-paginated across hosts and never holds
938
+ * the whole archived set. Pass `dryRun: true` first to get the count the
939
+ * confirm dialog quotes, then the same call with `dryRun: false` to delete.
940
+ *
941
+ * Removes Otto's records only — provider transcripts are left on disk. Gated
942
+ * by `server_info.features.historyDelete`; there is no fallback path, so check
943
+ * the flag before offering the action.
944
+ */
945
+ clearArchivedAgents(options: {
946
+ dryRun: boolean;
947
+ olderThanDays?: number;
948
+ requestId?: string;
949
+ }): Promise<HistoryAgentsClearArchivedResponse["payload"]>;
779
950
  archiveAgent(agentId: string): Promise<{
780
951
  archivedAt: string;
781
952
  }>;
@@ -823,6 +994,47 @@ export declare class DaemonClient {
823
994
  cancelRun(runId: string): Promise<boolean>;
824
995
  /** Orchestration: delete every finished (done/failed/canceled) run. */
825
996
  clearFinishedRuns(): Promise<string[]>;
997
+ /**
998
+ * Orchestration: delete one finished (or draft) run. Throws with the
999
+ * daemon's reason when it refuses — an active run has to be canceled first.
1000
+ */
1001
+ deleteRun(runId: string): Promise<string>;
1002
+ /** Orchestration: list the host's reusable graph templates. */
1003
+ listOrchestrationGraphs(): Promise<OrchestrationGraph[]>;
1004
+ /** Orchestration: upsert a graph template. Returns the persisted graph. */
1005
+ saveOrchestrationGraph(graph: OrchestrationGraph): Promise<OrchestrationGraph>;
1006
+ /** Orchestration: delete a graph template (built-in starters refuse). */
1007
+ deleteOrchestrationGraph(graphId: string): Promise<boolean>;
1008
+ /** Orchestration: list the host's reusable prompt templates and snippets. */
1009
+ listPromptTemplates(): Promise<PromptTemplate[]>;
1010
+ /** Orchestration: upsert a prompt template. Returns the persisted template. */
1011
+ savePromptTemplate(template: PromptTemplate): Promise<PromptTemplate>;
1012
+ /** Orchestration: delete a prompt template (built-in starters refuse). */
1013
+ deletePromptTemplate(templateId: string): Promise<boolean>;
1014
+ /**
1015
+ * Orchestration: start (or draft) a user-initiated orchestration. Returns the
1016
+ * run id (graph flavor) and the orchestrator chat's agent id to navigate to.
1017
+ */
1018
+ startOrchestration(input: {
1019
+ flavor: "ai" | "graph";
1020
+ cwd: string;
1021
+ workspaceId?: string;
1022
+ title?: string;
1023
+ description?: string;
1024
+ orchestratorPersonalityId?: string;
1025
+ orchestratorProvider?: string;
1026
+ orchestratorModel?: string;
1027
+ orchestratorThinkingOptionId?: string;
1028
+ prompt?: string;
1029
+ graphId?: string;
1030
+ graphInputs?: Record<string, string>;
1031
+ draft?: boolean;
1032
+ runId?: string;
1033
+ }): Promise<{
1034
+ runId?: string;
1035
+ agentId?: string;
1036
+ workspaceId?: string;
1037
+ }>;
826
1038
  updateAgent(agentId: string, updates: {
827
1039
  name?: string;
828
1040
  labels?: Record<string, string>;
@@ -844,8 +1056,26 @@ export declare class DaemonClient {
844
1056
  refreshAgent(agentId: string, requestId?: string): Promise<AgentRefreshedStatusPayload>;
845
1057
  fetchAgentTimeline(agentId: string, options?: FetchAgentTimelineOptions): Promise<FetchAgentTimelinePayload>;
846
1058
  buildAgentForkContext(agentId: string, options?: AgentForkContextOptions): Promise<AgentForkContextPayload>;
847
- sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
1059
+ sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<SendAgentMessageResult>;
848
1060
  sendMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
1061
+ /**
1062
+ * Pull one message back out of an agent's queue. Returns its text so the
1063
+ * caller can put it back in the composer, or null when the turn already
1064
+ * drained it. Requires `server_info.features.steerQueue`.
1065
+ */
1066
+ removeQueuedAgentMessage(agentId: string, messageId: string): Promise<{
1067
+ id: string;
1068
+ text: string;
1069
+ } | null>;
1070
+ /**
1071
+ * Move one queued message to a new position. Resolves false when the entry
1072
+ * was already drained or was already there — the authoritative order arrives
1073
+ * on the agent snapshot either way. Requires
1074
+ * `server_info.features.steerQueueReorder`.
1075
+ */
1076
+ reorderQueuedAgentMessage(agentId: string, messageId: string, toIndex: number): Promise<boolean>;
1077
+ /** Drop every message queued behind an agent's current turn. */
1078
+ clearAgentQueue(agentId: string): Promise<number>;
849
1079
  rewindAgent(agentId: string, messageId: string, mode: "conversation" | "files" | "both"): Promise<AgentRewindResponseMessage["payload"]>;
850
1080
  cancelAgent(agentId: string): Promise<{
851
1081
  cancelled?: boolean;
@@ -1042,6 +1272,13 @@ export declare class DaemonClient {
1042
1272
  readTextFile(cwd: string, path: string, requestId?: string): Promise<TextFileReadResult>;
1043
1273
  /** Conditional save — see FileWriteRequestSchema for the no-clobber contract. */
1044
1274
  writeFile(options: FileWriteOptions): Promise<FileWriteResult>;
1275
+ /** Create an empty file or a directory. Never overwrites — see FileCreateResultSchema. */
1276
+ createFileEntry(options: FileCreateOptions): Promise<FileCreateResult>;
1277
+ /** Permanent delete — an unlink, not a move to any trash. */
1278
+ deleteFileEntry(options: FileDeleteOptions): Promise<FileDeleteResult>;
1279
+ /** Rename, which is also move. Never clobbers an occupied destination. */
1280
+ renameFileEntry(options: FileRenameOptions): Promise<FileRenameResult>;
1281
+ refineFile(options: FileRefineOptions): Promise<FileRefineResult>;
1045
1282
  /**
1046
1283
  * Project-wide search. Per-file results stream through onFileResult (the
1047
1284
  * daemon emits them in order, before the summary response resolves); the
@@ -1052,6 +1289,81 @@ export declare class DaemonClient {
1052
1289
  listCodeFiles(cwd: string, requestId?: string): Promise<CodeListFilesResultPayload>;
1053
1290
  /** Name-based go-to-definition: one hit jumps, multiple hits are a picker. */
1054
1291
  findCodeSymbols(cwd: string, name: string, requestId?: string): Promise<CodeSymbolLocation[]>;
1292
+ /**
1293
+ * Language-server-backed go-to-definition. Unlike `findCodeSymbols` this resolves the
1294
+ * reference *at a position*, so multiple results mean real overloads or
1295
+ * implementations rather than "two files happen to use this name".
1296
+ *
1297
+ * Line and column are 1-based. Returns the whole payload, not just the locations,
1298
+ * because `indexing` and `unavailable` are answers the caller must show differently
1299
+ * from an empty result.
1300
+ */
1301
+ findCodeDefinition(input: CodeDefinitionQuery, requestId?: string): Promise<CodeDefinitionResult>;
1302
+ /**
1303
+ * Mirror the editor's current buffer to the daemon so definitions resolve against
1304
+ * unsaved edits. Debounced by the caller — this is not a per-keystroke RPC.
1305
+ */
1306
+ syncCodeDocument(cwd: string, path: string, text: string, requestId?: string): Promise<void>;
1307
+ /** Release the daemon-side mirror when a file tab closes. */
1308
+ closeCodeDocument(cwd: string, path: string, requestId?: string): Promise<void>;
1309
+ /**
1310
+ * The language server's own explanation of the symbol at a position. Returns the
1311
+ * whole payload: `indexing` and `unavailable` read differently to a user than "the
1312
+ * server had nothing to say", which is `ok` with a null `markdown`.
1313
+ */
1314
+ getCodeHover(input: CodeDefinitionQuery, requestId?: string): Promise<CodeHoverResult>;
1315
+ /** Every reference to the symbol at a position, for the references results tab. */
1316
+ findCodeReferences(input: CodeDefinitionQuery, requestId?: string): Promise<CodeReferencesResult>;
1317
+ /**
1318
+ * A rename **dry run** — every edit it would make, and nothing written. The client
1319
+ * puts this in front of the user as a job to audit before applying.
1320
+ */
1321
+ previewCodeRename(input: CodeRenamePreviewQuery, requestId?: string): Promise<CodeRenamePlan>;
1322
+ /**
1323
+ * Execute a rename the user audited. Sends the and NOT the edits: the daemon
1324
+ * recomputes the plan and refuses unless the identity still matches, which is what keeps
1325
+ * this from being an arbitrary-write RPC and what makes "what you approved is what
1326
+ * happens" enforceable rather than merely intended.
1327
+ */
1328
+ applyCodeRename(input: CodeRenameApplyQuery, requestId?: string): Promise<CodeRenameApplyOutcome>;
1329
+ /**
1330
+ * Take a rename run back. Sends only the run id: the daemon holds the before-images, and
1331
+ * restores a file only if it still holds exactly what the run wrote.
1332
+ */
1333
+ undoCodeRename(cwd: string, runId: string, requestId?: string): Promise<CodeRenameUndoOutcome>;
1334
+ /**
1335
+ * Live language-server state for the Daemon → Code screen: what this host can
1336
+ * supply, and what is running now. `cwd` scopes availability, since a server can
1337
+ * be present in one workspace's `node_modules` and absent in another's.
1338
+ */
1339
+ listLspServers(cwd: string, requestId?: string): Promise<LspServersSnapshot>;
1340
+ /** Stop one running language server. */
1341
+ stopLspServer(rootPath: string, serverId: string, requestId?: string): Promise<void>;
1342
+ /**
1343
+ * Solutions in a workspace, which is what decides whether the Files tab shows a view switcher
1344
+ * at all.
1345
+ *
1346
+ * Never throws and never carries an error the caller has to render. A workspace with no
1347
+ * solution, a host with no .NET SDK, and a host with the feature switched off all answer with an
1348
+ * empty list, so the caller has one silent case — "no switcher" — rather than four states.
1349
+ */
1350
+ listSolutions(cwd: string, requestId?: string): Promise<SolutionRef[]>;
1351
+ /** One solution's organisation: folders, the projects inside them, configurations. */
1352
+ getSolutionTree(input: {
1353
+ cwd: string;
1354
+ solutionPath: string;
1355
+ }, requestId?: string): Promise<SolutionTree>;
1356
+ /**
1357
+ * One project's evaluated file membership, fetched on expand.
1358
+ *
1359
+ * A `failed` status is a normal answer, not an exception: the daemon carries MSBuild's own
1360
+ * message for a project it refused, and one bad project must not blank the tree.
1361
+ */
1362
+ loadSolutionProject(input: {
1363
+ cwd: string;
1364
+ solutionPath: string;
1365
+ projectPath: string;
1366
+ }, requestId?: string): Promise<SolutionProjectContents>;
1055
1367
  /** Definition symbols for a single file (document outline). */
1056
1368
  getCodeOutline(cwd: string, path: string, requestId?: string): Promise<CodeSymbolLocation[]>;
1057
1369
  /** Preview-first project replace — see FileReplaceRequestSchema. */
@@ -1079,7 +1391,47 @@ export declare class DaemonClient {
1079
1391
  workspaceId: string;
1080
1392
  provider?: string;
1081
1393
  windowTokens?: number;
1394
+ personalityId?: string;
1082
1395
  }, requestId?: string): Promise<ContextReportGetResponseMessage["payload"]>;
1396
+ /**
1397
+ * A personality's accrued lessons plus the EXACT brief the daemon would inject
1398
+ * for `projectRoot`. The brief is returned rather than rebuilt client-side
1399
+ * because memory is only trustworthy if what you are shown is what is sent.
1400
+ */
1401
+ listPersonalityMemory(input: {
1402
+ personalityId: string;
1403
+ workspaceId?: string;
1404
+ projectRoot?: string;
1405
+ }, requestId?: string): Promise<PersonalityMemoryListResponseMessage["payload"]>;
1406
+ /**
1407
+ * Add (no `entryId`), edit, or forget (`drop`) one lesson.
1408
+ *
1409
+ * Pass `workspaceId` whenever the write may be project-scoped: the daemon
1410
+ * binds the entry to the repo root that workspace resolves to, and an entry
1411
+ * scoped to "project" with no root is filtered out of every brief — stored,
1412
+ * listed, and never sent.
1413
+ */
1414
+ updatePersonalityMemory(input: {
1415
+ personalityId: string;
1416
+ entryId?: string;
1417
+ text?: string;
1418
+ scope?: string;
1419
+ workspaceId?: string;
1420
+ projectRoot?: string;
1421
+ drop?: boolean;
1422
+ }, requestId?: string): Promise<PersonalityMemoryUpdateResponseMessage["payload"]>;
1423
+ /**
1424
+ * Resolve a deleted personality's lessons: move them to another personality or
1425
+ * discard them. Called BEFORE the roster write, so a failure leaves both the
1426
+ * personality and its memory intact.
1427
+ */
1428
+ transferPersonalityMemory(input: {
1429
+ fromPersonalityId: string;
1430
+ toPersonalityId?: string;
1431
+ mode: "transfer" | "delete";
1432
+ }, requestId?: string): Promise<PersonalityMemoryTransferResponseMessage["payload"]>;
1433
+ /** Per-personality lesson counts, for the accrual indicator and the selector. */
1434
+ getPersonalityMemoryStats(requestId?: string): Promise<PersonalityMemoryStatsResponseMessage["payload"]>;
1083
1435
  /** Rewrites one reference between "always loaded" and "link only". */
1084
1436
  requestContextEdgeConvert(input: {
1085
1437
  workspaceId: string;
@@ -1091,6 +1443,18 @@ export declare class DaemonClient {
1091
1443
  };
1092
1444
  target: "import" | "reference";
1093
1445
  }, requestId?: string): Promise<ContextEdgeConvertResponseMessage["payload"]>;
1446
+ /** Deletes every mechanically-fixable finding's range in one pass. */
1447
+ requestContextFindingsFix(input: {
1448
+ workspaceId: string;
1449
+ findings: Array<{
1450
+ filePath: string;
1451
+ range: {
1452
+ start: number;
1453
+ end: number;
1454
+ };
1455
+ snippet: string;
1456
+ }>;
1457
+ }, requestId?: string): Promise<ContextFindingsFixResponseMessage["payload"]>;
1094
1458
  listProviderModels(provider: AgentProvider, options?: {
1095
1459
  cwd?: string;
1096
1460
  requestId?: string;
@@ -1132,6 +1496,15 @@ export declare class DaemonClient {
1132
1496
  name: string;
1133
1497
  };
1134
1498
  }, requestId?: string): Promise<SpeechTtsPreviewResult>;
1499
+ speakMessage(params: {
1500
+ text: string;
1501
+ voice?: {
1502
+ provider?: string;
1503
+ model?: string;
1504
+ name: string;
1505
+ };
1506
+ }, requestId?: string): Promise<SpeechTtsSpeakResult>;
1507
+ cancelSpeakMessage(requestId?: string): Promise<SpeechTtsSpeakCancelResult>;
1135
1508
  generateVisualizerVoiceCues(params: {
1136
1509
  name: string;
1137
1510
  prompt?: string;
@@ -1291,6 +1664,14 @@ export declare class DaemonClient {
1291
1664
  waitForTerminalStreamEvent(predicate: (event: TerminalStreamEvent) => boolean, timeout?: number): Promise<TerminalStreamEvent>;
1292
1665
  private createRequestId;
1293
1666
  getLastServerInfoMessage(): ServerInfoStatusPayload | null;
1667
+ /**
1668
+ * Session totals for inbound daemon traffic, including the main-thread time
1669
+ * spent handling it. Null when runtime metrics are disabled for this client.
1670
+ * Read by the app's resource monitor — the wire is a first-class suspect when
1671
+ * the UI thread degrades, so it has to be measurable rather than inferred.
1672
+ */
1673
+ getTrafficTotals(): DaemonClientTrafficTotals | null;
1674
+ getTrafficHotspots(limit?: number): DaemonClientTrafficHotspot[];
1294
1675
  private resolveTransportUrlForAttempt;
1295
1676
  private sendHelloMessage;
1296
1677
  private disposeTransport;