@otto-code/client 0.8.12 → 0.8.14

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.
package/README.md CHANGED
@@ -1,12 +1,39 @@
1
1
  # @otto-code/client
2
2
 
3
- Otto's JavaScript/TypeScript client package.
3
+ TypeScript SDK for building integrations on top of a Otto daemon.
4
4
 
5
- ## Stability
5
+ ```bash
6
+ npm install @otto-code/client
7
+ ```
8
+
9
+ ```ts
10
+ import { createOttoClient } from "@otto-code/client";
11
+
12
+ const client = createOttoClient({ url: "ws://127.0.0.1:6868/ws" });
13
+ await client.connect();
14
+
15
+ const agent = await client.agents.create({
16
+ config: { provider: "codex/gpt-5.5" },
17
+ cwd: "/Users/me/dev/storefront",
18
+ prompt: "Review the current diff and name the riskiest change.",
19
+ });
20
+
21
+ const result = await agent.waitForFinish();
22
+ console.log(result.lastMessage);
23
+
24
+ await client.close();
25
+ ```
6
26
 
7
- This package is public so Otto's published packages can depend on it cleanly.
8
- It is not a stable public SDK yet.
27
+ The public API is the package root. Imports under `@otto-code/client/internal/*` are unsupported implementation details used by Otto's own packages.
28
+
29
+ Read the [SDK documentation](https://otto-code.me/docs/sdk) for agents, workspaces, provider discovery, events, recipes, and the API reference. Runnable TypeScript patterns also live in [`examples/`](./examples/README.md).
30
+
31
+ ## Runtime
32
+
33
+ The client needs a WebSocket implementation. Modern browsers and Node.js 22 provide one globally.
34
+
35
+ Use a WebSocket URL ending in `/ws`, such as `ws://127.0.0.1:6868/ws`. Pass `password` when the daemon requires authentication.
36
+
37
+ ## Stability
9
38
 
10
- APIs, exports, runtime behavior, and types may change or disappear in any
11
- release without advance notice. Use it outside Otto at your own risk until the
12
- package is explicitly documented as stable.
39
+ The high-level API exported from `@otto-code/client` is the supported SDK surface. The SDK and daemon remain protocol-compatible across versions, but newly added capabilities can require a newer daemon.
@@ -1,4 +1,5 @@
1
1
  import { normalizeAgentModelDefinition } from "@otto-code/protocol/agent-types";
2
+ import { expandProviderSnapshot } from "@otto-code/protocol/provider-snapshot-codec";
2
3
  // COMPAT(model-normalize): daemon normalizes at source (provider-registry) - shim covers older daemons; drop when floor >= v0.1.104
3
4
  function normalizeAgentModels(models) {
4
5
  if (!models) {
@@ -30,7 +31,9 @@ export function normalizeListProviderModelsPayload(payload) {
30
31
  return models === payload.models ? payload : { ...payload, models };
31
32
  }
32
33
  export function normalizeProvidersSnapshotPayload(payload) {
33
- const entries = normalizeProviderSnapshotEntries(payload.entries);
34
+ const entries = payload.compactSnapshot
35
+ ? expandProviderSnapshot(payload.compactSnapshot)
36
+ : normalizeProviderSnapshotEntries(payload.entries);
34
37
  return entries === payload.entries ? payload : { ...payload, entries };
35
38
  }
36
39
  export function normalizeProviderSnapshotUpdateMessage(msg) {
@@ -32,6 +32,28 @@ export interface DaemonClientTrafficHotspot {
32
32
  maxMs: number;
33
33
  bytes: number;
34
34
  }
35
+ /**
36
+ * One inbound session-message dispatch, retained only long enough to align it
37
+ * with a browser Long Animation Frame in the app's performance capture.
38
+ *
39
+ * `at` is an epoch timestamp so callers can compare it directly to the LoAF
40
+ * API. The phase timings are synchronous main-thread work and sum to
41
+ * approximately `totalMs`; small gaps are metric bookkeeping.
42
+ */
43
+ export interface DaemonClientInboundDispatchTiming {
44
+ at: number;
45
+ type: string;
46
+ /** Set for agent-scoped messages, so a capture can tell one hot agent from a spread. */
47
+ agentId?: string;
48
+ bytes: number;
49
+ decodeAndValidateMs: number;
50
+ internalDispatchMs: number;
51
+ rawListenersMs: number;
52
+ typedHandlersMs: number;
53
+ totalMs: number;
54
+ }
55
+ /** Bounded independently from rolling log buckets: this is capture evidence, not telemetry. */
56
+ export declare const INBOUND_DISPATCH_TIMING_CAPACITY = 500;
35
57
  export declare class DaemonClientRuntimeMetrics {
36
58
  private readonly logger;
37
59
  private readonly context;
@@ -49,11 +71,18 @@ export declare class DaemonClientRuntimeMetrics {
49
71
  private totalHandlerMs;
50
72
  private totalBinaryFrames;
51
73
  private readonly cumulativeByType;
74
+ private readonly inboundDispatchTimings;
52
75
  constructor(logger: RuntimeMetricsLogger, context: RuntimeMetricsContext, options?: RuntimeMetricsOptions);
53
76
  recordMessage(type: string, bytes: number, handlerMs: number): void;
54
77
  getTrafficTotals(): DaemonClientTrafficTotals;
55
78
  /** Inbound message types ranked by the main-thread time they have cost. */
56
79
  getTrafficHotspots(limit?: number): DaemonClientTrafficHotspot[];
80
+ recordInboundDispatch(timing: DaemonClientInboundDispatchTiming): void;
81
+ /**
82
+ * Recent inbound dispatches for a performance capture. Copies keep capture
83
+ * consumers from mutating a live client's bounded ring.
84
+ */
85
+ getInboundDispatchTimings(sinceMs?: number): DaemonClientInboundDispatchTiming[];
57
86
  private recordCumulative;
58
87
  recordAgentStream(payload: Extract<SessionOutboundMessage, {
59
88
  type: "agent_stream";
@@ -1,4 +1,6 @@
1
1
  const DEFAULT_ROLLING_WINDOW_MS = 60000;
2
+ /** Bounded independently from rolling log buckets: this is capture evidence, not telemetry. */
3
+ export const INBOUND_DISPATCH_TIMING_CAPACITY = 500;
2
4
  export class DaemonClientRuntimeMetrics {
3
5
  constructor(logger, context, options) {
4
6
  this.logger = logger;
@@ -17,6 +19,7 @@ export class DaemonClientRuntimeMetrics {
17
19
  this.totalHandlerMs = 0;
18
20
  this.totalBinaryFrames = 0;
19
21
  this.cumulativeByType = new Map();
22
+ this.inboundDispatchTimings = [];
20
23
  this.windowMs =
21
24
  typeof options?.windowMs === "number" && options.windowMs > 0
22
25
  ? options.windowMs
@@ -54,6 +57,21 @@ export class DaemonClientRuntimeMetrics {
54
57
  rows.sort((left, right) => right.totalMs - left.totalMs);
55
58
  return rows.slice(0, limit);
56
59
  }
60
+ recordInboundDispatch(timing) {
61
+ this.inboundDispatchTimings.push(cloneInboundDispatchTiming(timing));
62
+ if (this.inboundDispatchTimings.length > INBOUND_DISPATCH_TIMING_CAPACITY) {
63
+ this.inboundDispatchTimings.splice(0, this.inboundDispatchTimings.length - INBOUND_DISPATCH_TIMING_CAPACITY);
64
+ }
65
+ }
66
+ /**
67
+ * Recent inbound dispatches for a performance capture. Copies keep capture
68
+ * consumers from mutating a live client's bounded ring.
69
+ */
70
+ getInboundDispatchTimings(sinceMs) {
71
+ return this.inboundDispatchTimings
72
+ .filter((timing) => sinceMs === undefined || timing.at >= sinceMs)
73
+ .map(cloneInboundDispatchTiming);
74
+ }
57
75
  // Shared by JSON messages and binary frames; the per-sink totals
58
76
  // (`totalMessages` / `totalBinaryFrames`) are bumped by the callers so a
59
77
  // binary frame is never counted as both.
@@ -192,6 +210,19 @@ function cloneHandlerTimingMap(map) {
192
210
  { count: value.count, totalMs: value.totalMs, maxMs: value.maxMs },
193
211
  ]));
194
212
  }
213
+ function cloneInboundDispatchTiming(timing) {
214
+ return {
215
+ at: timing.at,
216
+ type: timing.type,
217
+ agentId: timing.agentId,
218
+ bytes: timing.bytes,
219
+ decodeAndValidateMs: timing.decodeAndValidateMs,
220
+ internalDispatchMs: timing.internalDispatchMs,
221
+ rawListenersMs: timing.rawListenersMs,
222
+ typedHandlersMs: timing.typedHandlersMs,
223
+ totalMs: timing.totalMs,
224
+ };
225
+ }
195
226
  function mergeCountMap(target, source) {
196
227
  for (const [key, value] of source) {
197
228
  incrementCount(target, key, value);
@@ -3,6 +3,10 @@ export declare function defaultWebSocketFactory(url: string, options?: {
3
3
  headers?: Record<string, string>;
4
4
  protocols?: string[];
5
5
  }): WebSocketLike;
6
+ export declare function nativeWebSocketFactory(url: string, options?: {
7
+ headers?: Record<string, string>;
8
+ protocols?: string[];
9
+ }): WebSocketLike;
6
10
  export declare function createWebSocketTransportFactory(factory: WebSocketFactory): DaemonTransportFactory;
7
11
  export declare function bindWsHandler(ws: WebSocketLike, event: "open" | "close" | "error" | "message", handler: (...args: unknown[]) => void): () => void;
8
12
  //# sourceMappingURL=daemon-client-websocket-transport.d.ts.map
@@ -1,10 +1,22 @@
1
1
  import { extractRelayMessage } from "./daemon-client-transport-utils.js";
2
- export function defaultWebSocketFactory(url, options) {
2
+ function getGlobalWebSocket() {
3
3
  const globalWs = globalThis.WebSocket;
4
4
  if (!globalWs) {
5
5
  throw new Error("WebSocket is not available in this runtime");
6
6
  }
7
- return new globalWs(url, options?.protocols);
7
+ return globalWs;
8
+ }
9
+ export function defaultWebSocketFactory(url, options) {
10
+ const globalWs = getGlobalWebSocket();
11
+ if (options?.protocols === undefined) {
12
+ return new globalWs(url);
13
+ }
14
+ return new globalWs(url, options.protocols);
15
+ }
16
+ // React Native and Node-compatible WebSocket implementations accept this extra options object.
17
+ // Keep it out of the standard browser factory above.
18
+ export function nativeWebSocketFactory(url, options) {
19
+ return new (getGlobalWebSocket())(url, options?.protocols, { headers: options?.headers });
8
20
  }
9
21
  export function createWebSocketTransportFactory(factory) {
10
22
  return ({ url, headers, protocols }) => {
@@ -3,14 +3,16 @@ import type { z } from "zod";
3
3
  import type { ProjectGithubCloneProtocol } from "@otto-code/protocol/messages";
4
4
  import { type ClientCapability } from "@otto-code/protocol/client-capabilities";
5
5
  import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ServerInfoStatusPayload } from "@otto-code/protocol/messages";
6
- 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, CheckoutGitFetchResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutGithubSetAutoMergeResponse, CheckoutGithubGetCheckDetailsResponse, PreviewListConfigResponse, PreviewStartResponse, PreviewBindTabResponse, PreviewStopResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, KanbanBoardsListResponse, ProjectKanbanTarget, KanbanBoardGetResponse, KanbanCardMoveResponse, KanbanCardCreateResponse, KanbanTaskLinkResponse, ValidateBranchResponse, BranchSuggestionsResponse, FileVersion, CheckoutCommit, ParsedDiffFile, WorkspaceRecoveryState, CheckoutForgeGetCheckDetailsResponse, CheckoutForgeSetAutoMergeResponse, ProjectCreateDirectoryResponse, FsFileWriteResult, FsFileWriteBinaryResult, GitHubSearchResponse, GitHubSearchRequest, ForgeSearchResponse, ForgeSearchRequest, GitHostingProviderId, HostingSearchRequest, HostingSearchResponse, HostingAuthStatusResponse, DirectorySuggestionsResponse, OttoWorktreeListResponse, OttoWorktreeArchiveResponse, ProjectIconResponse, ContextCategory, ContextPromptPreviewGetResponseMessage, ContextReportGetResponseMessage, ContextEdgeConvertResponseMessage, ContextFindingsFixResponseMessage, PersonalityMemoryListResponseMessage, PersonalityMemoryUpdateResponseMessage, PersonalityMemoryTransferResponseMessage, PersonalityMemoryStatsResponseMessage, ProjectKnowledgeListResponseMessage, ProjectKnowledgeGetResponseMessage, ProjectKnowledgeCreateResponseMessage, ProjectKnowledgeApplyResponseMessage, ProjectKnowledgeStatusResponseMessage, ProjectKnowledgeProjectApplyResponseMessage, ProjectKnowledgeReferenceApplyResponseMessage, ProjectKnowledgeRootApplyResponseMessage, ProjectKnowledgeDeleteResponseMessage, ProjectAddResponse, ProjectResolveWorkspaceForPathResponse, 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, HistoryAgentsStorageStatsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalCompatibilityDiagnosticResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, AgentPromptDelivery, TasksSuggestedStartMode, OttoConfigRaw, OttoConfigRevision, WorkspaceCreateRequest } from "@otto-code/protocol/messages";
6
+ 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, FileDownloadTokenResponse, FileEntryKind, FileEol, 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, CheckoutGitFetchResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutGithubSetAutoMergeResponse, CheckoutGithubGetCheckDetailsResponse, PreviewListConfigResponse, PreviewStartResponse, PreviewBindTabResponse, PreviewStopResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, KanbanBoardsListResponse, ProjectKanbanTarget, KanbanBoardGetResponse, KanbanCardMoveResponse, KanbanCardCreateResponse, KanbanTaskLinkResponse, ValidateBranchResponse, BranchSuggestionsResponse, FileVersion, CheckoutCommit, ParsedDiffFile, WorkspaceRecoveryState, CheckoutForgeGetCheckDetailsResponse, CheckoutForgeSetAutoMergeResponse, ProjectCreateDirectoryResponse, FsFileWriteResult, FsFileWriteBinaryResult, GitHubSearchResponse, GitHubSearchRequest, ForgeSearchResponse, ForgeSearchRequest, GitHostingProviderId, HostingSearchRequest, HostingSearchResponse, HostingAuthStatusResponse, DirectorySuggestionsResponse, OttoWorktreeListResponse, OttoWorktreeArchiveResponse, ProjectIconSource, ProjectIconResponse, ContextCategory, ContextPromptPreviewGetResponseMessage, ContextReportGetResponseMessage, ContextEdgeConvertResponseMessage, ContextFindingsFixResponseMessage, PersonalityMemoryListResponseMessage, PersonalityMemoryUpdateResponseMessage, PersonalityMemoryTransferResponseMessage, PersonalityMemoryStatsResponseMessage, ProjectKnowledgeListResponseMessage, ProjectKnowledgeGetResponseMessage, ProjectKnowledgeCreateResponseMessage, ProjectKnowledgeApplyResponseMessage, ProjectKnowledgeStatusResponseMessage, ProjectKnowledgeProjectApplyResponseMessage, ProjectKnowledgeReferenceApplyResponseMessage, ProjectKnowledgeRootApplyResponseMessage, ProjectKnowledgeDeleteResponseMessage, ProjectIconGetResponse, ProjectAddResponse, ProjectResolveWorkspaceForPathResponse, 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, HistoryAgentsStorageStatsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalCompatibilityDiagnosticResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, AgentPromptDelivery, TasksSuggestedStartMode, OttoConfigRaw, OttoConfigRevision, WorkspaceCreateRequest } from "@otto-code/protocol/messages";
7
7
  import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@otto-code/protocol/agent-types";
8
8
  import type { OrchestrationGraph, PromptTemplate, Run } from "@otto-code/protocol/orchestration";
9
9
  import type { BrainCatalogModel, BrainDiskUsage, BrainEvals, BrainHfSearchResult, BrainHostStatus, BrainInstalledModel, BrainInventoryModel, BrainJob, BrainLogsTailResponse, BrainLogsWatchResponse, BrainModelBudgetGetResponse, BrainModelDeleteResponse, BrainModelLoadResponse, BrainModelProfileGetResponse, BrainModelProfileSetResponse, BrainModelRenameResetResponse, BrainModelRenameResponse, BrainNetworkInfo, BrainRemoteConfig, BrainRepoQuant, BrainRuntime, ConnectorsListToolsResponse, ConnectorsOauthAuthorizeResponse, ConnectorsOauthDisconnectResponse, CommunicationsGetOverviewResponse, CommunicationsInboxGetHomeResponse, CommunicationsInboxNotificationsAcknowledgeResponse, CommunicationsInboxSearchResponse, CommunicationsInboxSetFavoriteResponse, CommunicationsInboxGetPresenceResponse, CommunicationsInboxGetMessagesResponse, CommunicationsInboxSetPresenceResponse, CommunicationsInboxSetEnabledResponse, CommunicationsInboxSendMessageResponse, CommunicationsRoomGetResponse, CommunicationsRoomThreadGetResponse, CommunicationsRoomMessageSendResponse, CommunicationsRoomReactionSetResponse, IntegrationsAuthorizationGetOverviewResponse, IntegrationsAuthorizationGetMethodsResponse, IntegrationsAuthorizationStartBrowserResponse, IntegrationsZoomStartAuthorizationResponse, CueMoment, MutableDaemonConfig, MutableDaemonConfigPatch, ProjectLink, SpeechSettingsOptions, SpeechTtsPreviewResult, SpeechTtsSpeakResult, SpeechTtsSpeakCancelResult, VisualizerVoiceCuesResult, AgentPersonalitiesGenerateProfileResult } from "@otto-code/protocol/messages";
10
+ import type { AgentConfigApply } from "@otto-code/protocol/messages";
10
11
  import { type DaemonTransportFactory, type WebSocketFactory } from "./daemon-client-transport.js";
11
- import { type DaemonClientTrafficHotspot, type DaemonClientTrafficTotals } from "./daemon-client-runtime-metrics.js";
12
+ import { type DaemonClientInboundDispatchTiming, type DaemonClientTrafficHotspot, type DaemonClientTrafficTotals } from "./daemon-client-runtime-metrics.js";
12
13
  import { type TerminalStreamEvent } from "./terminal-stream-router.js";
13
14
  import type { BrowserAutomationExecuteRequest, BrowserAutomationExecuteResponse } from "@otto-code/protocol/browser-automation/rpc-schemas";
15
+ export type { DaemonClientInboundDispatchTiming } from "./daemon-client-runtime-metrics.js";
14
16
  export interface Logger {
15
17
  debug(obj: object, msg?: string): void;
16
18
  info(obj: object, msg?: string): void;
@@ -127,8 +129,14 @@ export interface DaemonClientConfig {
127
129
  };
128
130
  runtimeMetricsIntervalMs?: number;
129
131
  runtimeMetricsWindowMs?: number;
132
+ trace?: DaemonClientTrace;
130
133
  capabilities?: Partial<Record<ClientCapability, unknown>>;
131
134
  }
135
+ export interface DaemonClientTrace {
136
+ isEnabled(): boolean;
137
+ beginSection(name: string, args?: Record<string, string>): void;
138
+ endSection(): void;
139
+ }
132
140
  export interface SendMessageOptions {
133
141
  messageId?: string;
134
142
  images?: Array<{
@@ -481,42 +489,6 @@ type CloseItemsPayload = CloseItemsResponse["payload"];
481
489
  type KillTerminalPayload = KillTerminalResponse["payload"];
482
490
  type CaptureTerminalPayload = CaptureTerminalResponse["payload"];
483
491
  type TerminalCompatibilityDiagnosticPayload = TerminalCompatibilityDiagnosticResponse["payload"];
484
- type ChatCreatePayload = Extract<SessionOutboundMessage, {
485
- type: "chat/create/response";
486
- }>["payload"];
487
- type ChatListPayload = Extract<SessionOutboundMessage, {
488
- type: "chat/list/response";
489
- }>["payload"];
490
- type ChatInspectPayload = Extract<SessionOutboundMessage, {
491
- type: "chat/inspect/response";
492
- }>["payload"];
493
- type ChatDeletePayload = Extract<SessionOutboundMessage, {
494
- type: "chat/delete/response";
495
- }>["payload"];
496
- type ChatPostPayload = Extract<SessionOutboundMessage, {
497
- type: "chat/post/response";
498
- }>["payload"];
499
- type ChatReadPayload = Extract<SessionOutboundMessage, {
500
- type: "chat/read/response";
501
- }>["payload"];
502
- type ChatWaitPayload = Extract<SessionOutboundMessage, {
503
- type: "chat/wait/response";
504
- }>["payload"];
505
- type LoopRunPayload = Extract<SessionOutboundMessage, {
506
- type: "loop/run/response";
507
- }>["payload"];
508
- type LoopListPayload = Extract<SessionOutboundMessage, {
509
- type: "loop/list/response";
510
- }>["payload"];
511
- type LoopInspectPayload = Extract<SessionOutboundMessage, {
512
- type: "loop/inspect/response";
513
- }>["payload"];
514
- type LoopLogsPayload = Extract<SessionOutboundMessage, {
515
- type: "loop/logs/response";
516
- }>["payload"];
517
- type LoopStopPayload = Extract<SessionOutboundMessage, {
518
- type: "loop/stop/response";
519
- }>["payload"];
520
492
  type ScheduleCreatePayload = Extract<SessionOutboundMessage, {
521
493
  type: "schedule/create/response";
522
494
  }>["payload"];
@@ -584,9 +556,13 @@ export interface FetchAgentTimelineOptions {
584
556
  cursor?: FetchAgentTimelineCursor;
585
557
  limit?: number;
586
558
  projection?: FetchAgentTimelineProjection;
559
+ mergeWindow?: boolean;
587
560
  requestId?: string;
588
561
  timeout?: number;
589
562
  }
563
+ export type AgentTimelinePromptIndexPayload = Extract<SessionOutboundMessage, {
564
+ type: "agent.timeline.list_prompts.response";
565
+ }>["payload"];
590
566
  export type ProviderSubagentListPayload = Extract<SessionOutboundMessage, {
591
567
  type: "agent.provider_subagents.list.response";
592
568
  }>["payload"];
@@ -665,70 +641,6 @@ export type FetchWorkspacesOptions = Omit<FetchWorkspacesRequest, "type" | "requ
665
641
  };
666
642
  export type FetchWorkspacesEntry = FetchWorkspacesPayload["entries"][number];
667
643
  export type FetchWorkspacesPageInfo = FetchWorkspacesPayload["pageInfo"];
668
- export interface CreateChatRoomOptions {
669
- name: string;
670
- purpose?: string | null;
671
- requestId?: string;
672
- }
673
- export interface InspectChatRoomOptions {
674
- room: string;
675
- requestId?: string;
676
- }
677
- export interface DeleteChatRoomOptions {
678
- room: string;
679
- requestId?: string;
680
- }
681
- export interface PostChatMessageOptions {
682
- room: string;
683
- body: string;
684
- authorAgentId?: string;
685
- replyToMessageId?: string | null;
686
- requestId?: string;
687
- }
688
- export interface ReadChatMessagesOptions {
689
- room: string;
690
- limit?: number;
691
- since?: string;
692
- authorAgentId?: string;
693
- requestId?: string;
694
- timeout?: number;
695
- }
696
- export interface WaitForChatMessagesOptions {
697
- room: string;
698
- afterMessageId?: string | null;
699
- timeoutMs?: number;
700
- requestId?: string;
701
- }
702
- export interface RunLoopOptions {
703
- prompt: string;
704
- cwd: string;
705
- provider?: string;
706
- model?: string;
707
- modeId?: string;
708
- verifierProvider?: string;
709
- verifierModel?: string;
710
- verifierModeId?: string;
711
- verifyPrompt?: string | null;
712
- verifyChecks?: string[];
713
- name?: string | null;
714
- sleepMs?: number;
715
- maxIterations?: number;
716
- maxTimeMs?: number;
717
- requestId?: string;
718
- }
719
- export interface InspectLoopOptions {
720
- id: string;
721
- requestId?: string;
722
- }
723
- export interface LoopLogsOptions {
724
- id: string;
725
- afterSeq?: number;
726
- requestId?: string;
727
- }
728
- export interface StopLoopOptions {
729
- id: string;
730
- requestId?: string;
731
- }
732
644
  export interface CreateScheduleOptions {
733
645
  prompt: string;
734
646
  name?: string | null;
@@ -759,11 +671,7 @@ export interface CreateScheduleOptions {
759
671
  archiveOnFinish?: boolean;
760
672
  isolation?: "local" | "worktree";
761
673
  title?: string | null;
762
- approvalPolicy?: string;
763
- sandboxMode?: string;
764
- networkAccess?: boolean;
765
- webSearch?: boolean;
766
- extra?: AgentSessionConfig["extra"];
674
+ providerOptions?: AgentSessionConfig["providerOptions"];
767
675
  systemPrompt?: string;
768
676
  mcpServers?: AgentSessionConfig["mcpServers"];
769
677
  };
@@ -840,6 +748,21 @@ export interface WaitForFinishResult {
840
748
  error: string | null;
841
749
  lastMessage: string | null;
842
750
  }
751
+ type GetDaemonConfigResponse = Extract<SessionOutboundMessage, {
752
+ type: "get_daemon_config_response";
753
+ }>;
754
+ type SetDaemonConfigResponse = Extract<SessionOutboundMessage, {
755
+ type: "set_daemon_config_response";
756
+ }>;
757
+ type CorrelatedResponseMessage = Extract<SessionOutboundMessage, {
758
+ payload: {
759
+ requestId: string;
760
+ };
761
+ }> | GetDaemonConfigResponse | SetDaemonConfigResponse;
762
+ type CorrelatedResponseType = CorrelatedResponseMessage["type"];
763
+ type CorrelatedResponsePayload<TType extends CorrelatedResponseType> = Extract<CorrelatedResponseMessage, {
764
+ type: TType;
765
+ }>["payload"];
843
766
  type ProjectGithubClonePayload = Extract<SessionOutboundMessage, {
844
767
  type: "project.github.clone.response";
845
768
  }>["payload"];
@@ -904,6 +827,11 @@ export declare class DaemonClient {
904
827
  type: TType;
905
828
  }>) => void): () => void;
906
829
  on(handler: DaemonEventHandler): () => void;
830
+ private beginTraceSection;
831
+ private endTraceSection;
832
+ private traceInstant;
833
+ private sendJsonMessage;
834
+ private sendTransportFrame;
907
835
  /**
908
836
  * Send a session message. For fire-and-forget messages (heartbeats, etc.),
909
837
  * failures are suppressed if `suppressSendErrors` is configured.
@@ -942,6 +870,7 @@ export declare class DaemonClient {
942
870
  appVisibilityChangedAt?: string;
943
871
  }): void;
944
872
  registerPushToken(token: string): void;
873
+ unregisterPushToken(token: string): Promise<void>;
945
874
  ping(params?: {
946
875
  requestId?: string;
947
876
  timeoutMs?: number;
@@ -1150,6 +1079,7 @@ export declare class DaemonClient {
1150
1079
  }, requestId?: string): Promise<{
1151
1080
  target: ProjectKanbanTarget | null;
1152
1081
  }>;
1082
+ setProjectIcon(projectId: string, source: ProjectIconSource, requestId?: string): Promise<void>;
1153
1083
  removeProject(projectId: string, requestId?: string): Promise<{
1154
1084
  removedWorkspaceIds: string[];
1155
1085
  }>;
@@ -1256,6 +1186,10 @@ export declare class DaemonClient {
1256
1186
  unsubscribe: () => void;
1257
1187
  }>;
1258
1188
  fetchAgentTimeline(agentId: string, options?: FetchAgentTimelineOptions): Promise<FetchAgentTimelinePayload>;
1189
+ listAgentTimelinePrompts(agentId: string, options?: {
1190
+ requestId?: string;
1191
+ timeout?: number;
1192
+ }): Promise<AgentTimelinePromptIndexPayload>;
1259
1193
  buildAgentForkContext(agentId: string, options?: AgentForkContextOptions): Promise<AgentForkContextPayload>;
1260
1194
  sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<SendAgentMessageResult>;
1261
1195
  sendMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
@@ -1293,6 +1227,14 @@ export declare class DaemonClient {
1293
1227
  * server_info.features.setAgentPersonality.
1294
1228
  */
1295
1229
  setAgentPersonality(agentId: string, personalityId: string | null): Promise<AgentProviderNotice | null>;
1230
+ /**
1231
+ * Applies a whole agent-config bundle in one request. Use this instead of
1232
+ * chaining the single-field setters when the values belong together so client
1233
+ * interruption and other mutations cannot interleave between steps. A
1234
+ * provider rejection can still leave earlier steps applied.
1235
+ * Gated on `server_info.features.agentConfigApply`.
1236
+ */
1237
+ applyAgentConfig(agentId: string, config: AgentConfigApply): Promise<AgentProviderNotice | null>;
1296
1238
  restartServer(reason?: string, requestId?: string): Promise<RestartRequestedStatusPayload>;
1297
1239
  shutdownServer(options?: ShutdownServerOptions): Promise<ShutdownRequestedStatusPayload>;
1298
1240
  updateDaemon(requestId?: string): Promise<DaemonUpdateResponse["payload"]>;
@@ -1508,12 +1450,6 @@ export declare class DaemonClient {
1508
1450
  * frames leave in order and behind the JSON request that announced them.
1509
1451
  */
1510
1452
  private sendFileTransfer;
1511
- /** Create an empty file or a directory. Never overwrites - see FileCreateResultSchema. */
1512
- createFileEntry(options: FileCreateOptions): Promise<FileCreateResult>;
1513
- /** Permanent delete - an unlink, not a move to any trash. */
1514
- deleteFileEntry(options: FileDeleteOptions): Promise<FileDeleteResult>;
1515
- /** Rename, which is also move. Never clobbers an occupied destination. */
1516
- renameFileEntry(options: FileRenameOptions): Promise<FileRenameResult>;
1517
1453
  refineFile(options: FileRefineOptions): Promise<FileRefineResult>;
1518
1454
  /**
1519
1455
  * Project-wide search. Per-file results stream through onFileResult (the
@@ -1795,6 +1731,7 @@ export declare class DaemonClient {
1795
1731
  }): Promise<ListAvailableProvidersPayload>;
1796
1732
  getProvidersSnapshot(options?: {
1797
1733
  cwd?: string;
1734
+ ifNoneMatch?: string;
1798
1735
  requestId?: string;
1799
1736
  }): Promise<GetProvidersSnapshotPayload>;
1800
1737
  getDaemonConfig(requestId?: string): Promise<{
@@ -1954,6 +1891,28 @@ export declare class DaemonClient {
1954
1891
  brainCalibrate(model: string, requestId?: string): Promise<BrainJob>;
1955
1892
  brainSweep(model: string, requestId?: string): Promise<BrainJob>;
1956
1893
  brainBench(model?: string | null, requestId?: string): Promise<BrainJob>;
1894
+ createFileEntry(input: {
1895
+ cwd: string;
1896
+ parentPath: string;
1897
+ name: string;
1898
+ kind: "file" | "directory";
1899
+ }): Promise<CorrelatedResponsePayload<"fs.entry.create.response">>;
1900
+ renameFileEntry(input: {
1901
+ cwd: string;
1902
+ path: string;
1903
+ name: string;
1904
+ }): Promise<CorrelatedResponsePayload<"fs.entry.rename.response">>;
1905
+ duplicateFileEntry(input: {
1906
+ cwd: string;
1907
+ path: string;
1908
+ }): Promise<CorrelatedResponsePayload<"fs.entry.duplicate.response">>;
1909
+ deleteFileEntry(input: {
1910
+ cwd: string;
1911
+ path: string;
1912
+ }): Promise<CorrelatedResponsePayload<"fs.entry.delete.response">>;
1913
+ checkoutDiscardChanges(cwd: string, input: {
1914
+ paths: string[];
1915
+ }): Promise<CorrelatedResponsePayload<"checkout.discard_changes.response">>;
1957
1916
  brainJobsList(requestId?: string): Promise<BrainJob[]>;
1958
1917
  brainJobsCancel(jobId: string, requestId?: string): Promise<BrainJob[]>;
1959
1918
  /**
@@ -1986,6 +1945,7 @@ export declare class DaemonClient {
1986
1945
  brainModelLoad(modelId: string, requestId?: string): Promise<BrainModelLoadResponse["payload"]>;
1987
1946
  /** Unload the resident model, leaving the brain up and serving nothing. */
1988
1947
  brainModelUnload(requestId?: string): Promise<BrainHostStatus | null>;
1948
+ getProjectIcon(projectId: string, requestId?: string): Promise<ProjectIconGetResponse["payload"]>;
1989
1949
  /** Delete a model's files. The brain refuses while that model is loaded. */
1990
1950
  brainModelDelete(modelId: string, requestId?: string): Promise<BrainModelDeleteResponse["payload"]>;
1991
1951
  brainModelComponentDelete(modelId: string, componentId: string, requestId?: string): Promise<{
@@ -2126,13 +2086,6 @@ export declare class DaemonClient {
2126
2086
  stripAnsi?: boolean;
2127
2087
  }, requestId?: string): Promise<CaptureTerminalPayload>;
2128
2088
  runTerminalCompatibilityDiagnostic(requestId?: string): Promise<TerminalCompatibilityDiagnosticPayload>;
2129
- createChatRoom(options: CreateChatRoomOptions): Promise<ChatCreatePayload>;
2130
- listChatRooms(requestId?: string): Promise<ChatListPayload>;
2131
- inspectChatRoom(options: InspectChatRoomOptions): Promise<ChatInspectPayload>;
2132
- deleteChatRoom(options: DeleteChatRoomOptions): Promise<ChatDeletePayload>;
2133
- postChatMessage(options: PostChatMessageOptions): Promise<ChatPostPayload>;
2134
- readChatMessages(options: ReadChatMessagesOptions): Promise<ChatReadPayload>;
2135
- waitForChatMessages(options: WaitForChatMessagesOptions): Promise<ChatWaitPayload>;
2136
2089
  scheduleCreate(options: CreateScheduleOptions): Promise<ScheduleCreatePayload>;
2137
2090
  scheduleList(requestId?: string): Promise<ScheduleListPayload>;
2138
2091
  scheduleInspect(options: InspectScheduleOptions): Promise<ScheduleInspectPayload>;
@@ -2193,11 +2146,6 @@ export declare class DaemonClient {
2193
2146
  artifactId: string;
2194
2147
  requestId?: string;
2195
2148
  }): Promise<ArtifactGetContentPayload>;
2196
- loopRun(options: RunLoopOptions): Promise<LoopRunPayload>;
2197
- loopList(requestId?: string): Promise<LoopListPayload>;
2198
- loopInspect(options: string | InspectLoopOptions): Promise<LoopInspectPayload>;
2199
- loopLogs(options: string | LoopLogsOptions, afterSeq?: number): Promise<LoopLogsPayload>;
2200
- loopStop(options: string | StopLoopOptions): Promise<LoopStopPayload>;
2201
2149
  onTerminalStreamEvent(handler: (event: TerminalStreamEvent) => void): () => void;
2202
2150
  waitForTerminalStreamEvent(predicate: (event: TerminalStreamEvent) => boolean, timeout?: number): Promise<TerminalStreamEvent>;
2203
2151
  private createRequestId;
@@ -2210,6 +2158,12 @@ export declare class DaemonClient {
2210
2158
  */
2211
2159
  getTrafficTotals(): DaemonClientTrafficTotals | null;
2212
2160
  getTrafficHotspots(limit?: number): DaemonClientTrafficHotspot[];
2161
+ /**
2162
+ * Bounded, timestamped dispatch phases for matching an inbound daemon
2163
+ * message to a browser Long Animation Frame. This is diagnostic evidence,
2164
+ * not a protocol surface.
2165
+ */
2166
+ getInboundDispatchTimings(sinceMs?: number): DaemonClientInboundDispatchTiming[];
2213
2167
  private resolveTransportUrlForAttempt;
2214
2168
  private sendHelloMessage;
2215
2169
  private disposeTransport;