@camerontaylor/paseo-client 0.8.0-fork.1 → 0.9.0-beta.2.fork.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.
@@ -14,6 +14,8 @@ export class CreationClient {
14
14
  if (request.workspaceId || request.agent?.agentId)
15
15
  throw new Error("Update the host to use caller-selected creation IDs.");
16
16
  const { agent, ...workspaceInput } = request;
17
+ const sourceCwd = request.source.kind === "directory" ? request.source.path : request.source.cwd;
18
+ const relativeCwd = agent && sourceCwd ? relativeDirectory(agent.config.cwd, sourceCwd) : undefined;
17
19
  const workspace = await this.deps.legacyWorkspace(workspaceInput);
18
20
  if (workspace.error || !workspace.workspace)
19
21
  return workspace;
@@ -30,10 +32,7 @@ export class CreationClient {
30
32
  });
31
33
  if (!agent)
32
34
  return workspace;
33
- const sourceCwd = request.source.kind === "directory" ? request.source.path : request.source.cwd;
34
- const cwd = sourceCwd
35
- ? remapDirectory(agent.config.cwd, sourceCwd, workspace.workspace.workspaceDirectory)
36
- : workspace.workspace.workspaceDirectory;
35
+ const cwd = `${workspace.workspace.workspaceDirectory.replace(/[\\/]+$/, "")}${relativeCwd ?? relativeDirectory(agent.config.cwd, workspace.workspace.projectRootPath)}`;
37
36
  const created = await this.legacyAgent({
38
37
  ...agent,
39
38
  config: { ...agent.config, cwd },
@@ -46,23 +45,13 @@ export class CreationClient {
46
45
  async legacyAgent(input) {
47
46
  if (input.agentId)
48
47
  throw new Error("Update the host to use caller-selected creation IDs.");
48
+ const { idempotencyKey: _key, ...unkeyed } = input;
49
+ const hasPrompt = Boolean(input.initialPrompt || input.images?.length || input.attachments?.length);
49
50
  // COMPAT(creationLifecycle): added in v0.8.0, remove after 2027-03-11 once the daemon floor supports creationLifecycle.
50
- // Old keyed creates reject initialPrompt. Keep this adaptation private to the SDK.
51
- if (input.idempotencyKey) {
52
- const { initialPrompt = "", images, attachments, ...creation } = input;
53
- const hasPrompt = Boolean(initialPrompt.trim() || images?.length || attachments?.length);
54
- if (hasPrompt && input.outputSchema)
55
- throw new Error("Update the host to create a keyed agent with structured initial output.");
56
- const agent = await this.deps.legacyAgent(creation);
57
- if (hasPrompt)
58
- await this.deps.sendMessage(agent.id, initialPrompt, {
59
- messageId: input.clientMessageId ?? `${input.idempotencyKey}:initial-message`,
60
- images,
61
- attachments,
62
- });
63
- return { agent, error: null };
64
- }
65
- return { agent: await this.deps.legacyAgent(input), error: null };
51
+ // Preserve the original create-and-prompt RPC: old hosts use its prompt for
52
+ // titles, creation context and startup. Their keyed create cannot accept it.
53
+ const request = !hasPrompt && this.deps.supports("agentRequestReceipts") ? input : unkeyed;
54
+ return { agent: await this.deps.legacyAgent(request), error: null };
66
55
  }
67
56
  start(kind, input, observer, legacy) {
68
57
  const key = typeof input.idempotencyKey === "string" ? input.idempotencyKey : this.deps.requestId();
@@ -98,7 +87,7 @@ export class CreationClient {
98
87
  resolve,
99
88
  reject,
100
89
  settled: false,
101
- modern: this.deps.supports(),
90
+ modern: this.deps.supports("creationLifecycle"),
102
91
  recovering: false,
103
92
  };
104
93
  this.operations.set(identity, operation);
@@ -119,38 +108,22 @@ export class CreationClient {
119
108
  this.finish(operation, result);
120
109
  }
121
110
  catch (error) {
122
- if (operation.settled || !this.deps.connected())
111
+ if (operation.settled ||
112
+ (error instanceof Error && "code" in error && error.code === "DAEMON_CONNECTION_LOST"))
123
113
  return;
124
114
  this.fail(operation, error);
125
115
  }
126
116
  }
127
117
  receive(snapshot) {
128
118
  const operation = this.operations.get(JSON.stringify([snapshot.kind, snapshot.idempotencyKey]));
129
- if (!operation || (operation.snapshot && operation.snapshot.revision >= snapshot.revision))
130
- return;
131
- operation.snapshot = snapshot;
132
- for (const observer of operation.observers)
133
- this.notify(observer, snapshot);
134
- if (operation.recovering && (snapshot.phase === "completed" || snapshot.phase === "failed")) {
135
- this.finish(operation, {
136
- agent: snapshot.agent,
137
- workspace: snapshot.workspace,
138
- creation: snapshot,
139
- setupSkippedReason: snapshot.setupSkippedReason,
140
- error: snapshot.error,
141
- errorCode: snapshot.errorCode,
142
- });
143
- }
144
- }
145
- async recover(operation) {
146
- operation.recovering = true;
147
- const snapshot = await this.deps.subscribe(operation.kind, operation.key);
148
- if (!snapshot) {
149
- await this.submit(operation);
119
+ if (!operation || (operation.snapshot && operation.snapshot.revision > snapshot.revision))
150
120
  return;
121
+ if (operation.snapshot?.revision !== snapshot.revision) {
122
+ operation.snapshot = snapshot;
123
+ for (const observer of operation.observers)
124
+ this.notify(observer, snapshot);
151
125
  }
152
- this.receive(snapshot);
153
- if (snapshot.phase === "completed" || snapshot.phase === "failed") {
126
+ if (operation.recovering && (snapshot.phase === "completed" || snapshot.phase === "failed")) {
154
127
  this.finish(operation, {
155
128
  agent: snapshot.agent,
156
129
  workspace: snapshot.workspace,
@@ -163,12 +136,17 @@ export class CreationClient {
163
136
  }
164
137
  reconnect() {
165
138
  for (const operation of this.operations.values()) {
166
- if (operation.settled || !operation.modern)
139
+ if (operation.settled || !operation.modern || operation.recovering)
167
140
  continue;
168
- void this.recover(operation).catch((error) => {
169
- if (this.deps.connected())
170
- this.fail(operation, error);
171
- });
141
+ operation.recovering = true;
142
+ operation.stopObserving = this.deps.observe(operation.kind, operation.key, (snapshot) => {
143
+ if (!snapshot)
144
+ void this.submit(operation);
145
+ else {
146
+ // Re-subscription is authoritative even if its revision matches the last live event.
147
+ this.receive(snapshot);
148
+ }
149
+ }, (error) => this.fail(operation, error));
172
150
  }
173
151
  }
174
152
  close() {
@@ -181,8 +159,7 @@ export class CreationClient {
181
159
  if (operation.settled)
182
160
  return;
183
161
  operation.settled = true;
184
- if (operation.recovering)
185
- void this.deps.subscribe(operation.kind, operation.key, false).catch(() => undefined);
162
+ operation.stopObserving?.();
186
163
  operation.resolve(result);
187
164
  operation.observers.clear();
188
165
  this.operations.delete(JSON.stringify([operation.kind, operation.key]));
@@ -191,6 +168,7 @@ export class CreationClient {
191
168
  if (operation.settled)
192
169
  return;
193
170
  operation.settled = true;
171
+ operation.stopObserving?.();
194
172
  operation.reject(error instanceof Error ? error : new Error(String(error)));
195
173
  operation.observers.clear();
196
174
  this.operations.delete(JSON.stringify([operation.kind, operation.key]));
@@ -204,13 +182,16 @@ export class CreationClient {
204
182
  }
205
183
  }
206
184
  }
207
- function remapDirectory(cwd, source, target) {
208
- const normalize = (value) => value.replace(/\\/g, "/").replace(/\/$/, "");
185
+ function relativeDirectory(cwd, source) {
186
+ const normalize = (value) => value.replace(/\\/g, "/").replace(/\/+$/, "");
209
187
  const normalized = normalize(cwd), root = normalize(source);
188
+ // Parent traversal must never survive mapping into the newly created checkout.
189
+ if ([normalized, root].some((path) => path.split("/").includes("..")))
190
+ throw new Error("Agent directory must be inside the workspace source");
210
191
  if (normalized === root)
211
- return target;
192
+ return "";
212
193
  if (!normalized.startsWith(`${root}/`))
213
194
  throw new Error("Agent directory must be inside the workspace source");
214
- return `${normalize(target)}${normalized.slice(root.length)}`;
195
+ return normalized.slice(root.length);
215
196
  }
216
197
  //# sourceMappingURL=index.js.map
@@ -1,10 +1,12 @@
1
- import { type TimelineSubscription } from "./connection/index.js";
1
+ import { type TimelineMessage } from "./timeline-subscription/index.js";
2
+ import { type OwnedSubscription, type TimelineSubscription } from "./connection/index.js";
2
3
  import type { CreationSnapshot } from "@camerontaylor/paseo-protocol/messages";
3
4
  import type { z } from "zod";
4
- import { type ClientCapability } from "@camerontaylor/paseo-protocol/client-capabilities";
5
+ import type { SessionEventSubscription } from "@camerontaylor/paseo-protocol/messages";
6
+ import type { ClientCapability } from "@camerontaylor/paseo-protocol/client-capabilities";
5
7
  import type { AgentAttentionNotificationPayload } from "@camerontaylor/paseo-protocol/agent-attention-notification";
6
8
  import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ActiveTurnBehavior, type ServerInfoStatusPayload } from "@camerontaylor/paseo-protocol/messages";
7
- import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreatePaseoWorktreeRequest, FileDownloadTokenResponse, FileUploadResponse, FileExplorerResponse, FileVersion, FileWriteResult, FetchAgentTimelineResponseMessage, AgentForkContextResponseMessage, GitSetupOptions, CheckoutStatusResponse, CheckoutCommit, ParsedDiffFile, CheckoutCommitResponse, CheckoutMergeResponse, CheckoutMergeFromBaseResponse, CheckoutPullResponse, CheckoutPushResponse, CheckoutRefreshResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutForgeSetAutoMergeResponse, CheckoutGithubSetAutoMergeResponse, CheckoutForgeGetCheckDetailsResponse, CheckoutGithubGetCheckDetailsResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, ValidateBranchResponse, BranchSuggestionsResponse, ForgeSearchResponse, ForgeSearchRequest, GitHubSearchResponse, GitHubSearchRequest, DirectorySuggestionsResponse, PaseoWorktreeListResponse, PaseoWorktreeArchiveResponse, ProjectIconSource, ProjectIconResponse, ProjectIconGetResponse, ProjectAddResponse, ProjectCreateDirectoryResponse, OpenProjectResponseMessage, WorkspaceGithubSearchRepositoriesResponse, ProjectGithubCloneProtocol, ProjectGithubCloneResponse, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DaemonConfigReloadResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest, WorkspaceRecoveryState, PluginListItem, PluginLogEntry, PluginSourceStatusItem, PluginSourceUpdateItem, AgentSkillSelection, AgentSkillsStatus, AgentSkillsSaveResult } from "@camerontaylor/paseo-protocol/messages";
9
+ import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreatePaseoWorktreeRequest, FileDownloadTokenResponse, FileUploadResponse, FileExplorerResponse, FileVersion, FileWriteResult, FetchAgentTimelineResponseMessage, AgentForkContextResponseMessage, GitSetupOptions, CheckoutStatusResponse, CheckoutCommit, ParsedDiffFile, CheckoutCommitResponse, CheckoutMergeResponse, CheckoutMergeFromBaseResponse, CheckoutPullResponse, CheckoutPushResponse, CheckoutRefreshResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutForgeSetAutoMergeResponse, CheckoutGithubSetAutoMergeResponse, CheckoutForgeGetCheckDetailsResponse, CheckoutGithubGetCheckDetailsResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, ValidateBranchResponse, BranchSuggestionsResponse, ForgeSearchResponse, ForgeSearchRequest, GitHubSearchResponse, GitHubSearchRequest, DirectorySuggestionsResponse, PaseoWorktreeListResponse, PaseoWorktreeArchiveResponse, ProjectIconSource, ProjectIconResponse, ProjectIconGetResponse, ProjectAddResponse, ProjectCreateDirectoryResponse, OpenProjectResponseMessage, WorkspaceGithubSearchRepositoriesResponse, ProjectGithubCloneProtocol, ProjectGithubCloneResponse, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DaemonConfigReloadResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest, WorkspaceRecoveryState, PluginListItem, PluginLogEntry, PluginSourceStatusItem, PluginSourceUpdateItem, PluginUpdateSelection, PluginUpdateProposal, PluginUpdatePreview, PluginUpdateResult, AgentSkillSelection, AgentSkillsStatus, AgentSkillsSaveResult } from "@camerontaylor/paseo-protocol/messages";
8
10
  import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@camerontaylor/paseo-protocol/agent-types";
9
11
  import type { AgentConfigApply, MutableDaemonConfig, MutableDaemonConfigPatch } from "@camerontaylor/paseo-protocol/messages";
10
12
  import { type DaemonTransportFactory, type WebSocketFactory } from "./daemon-client-transport.js";
@@ -336,6 +338,14 @@ export interface FetchAgentTimelineOptions {
336
338
  requestId?: string;
337
339
  timeout?: number;
338
340
  }
341
+ export interface AgentTimelineSearchOptions {
342
+ agentId: string;
343
+ query: string;
344
+ cursor?: number;
345
+ }
346
+ export type AgentTimelineSearchPayload = Extract<SessionOutboundMessage, {
347
+ type: "agent.timeline.search.response";
348
+ }>["payload"];
339
349
  export type AgentTimelinePromptIndexPayload = Extract<SessionOutboundMessage, {
340
350
  type: "agent.timeline.list_prompts.response";
341
351
  }>["payload"];
@@ -389,6 +399,7 @@ type FetchAgentsRequest = Extract<SessionInboundMessage, {
389
399
  type: "fetch_agents_request";
390
400
  }>;
391
401
  export type FetchAgentsOptions = Omit<FetchAgentsRequest, "type" | "requestId"> & {
402
+ signal?: AbortSignal;
392
403
  requestId?: string;
393
404
  timeout?: number;
394
405
  };
@@ -422,6 +433,7 @@ type FetchWorkspacesRequest = Extract<SessionInboundMessage, {
422
433
  type: "fetch_workspaces_request";
423
434
  }>;
424
435
  export type FetchWorkspacesOptions = Omit<FetchWorkspacesRequest, "type" | "requestId"> & {
436
+ signal?: AbortSignal;
425
437
  requestId?: string;
426
438
  };
427
439
  export type FetchWorkspacesEntry = FetchWorkspacesPayload["entries"][number];
@@ -549,15 +561,22 @@ type CorrelatedResponseMessage = Extract<SessionOutboundMessage, {
549
561
  payload: {
550
562
  requestId: string;
551
563
  };
564
+ }> | Extract<SessionOutboundMessage, {
565
+ type: "terminals_changed";
552
566
  }> | GetDaemonConfigResponse | SetDaemonConfigResponse;
553
567
  type CorrelatedResponseType = CorrelatedResponseMessage["type"];
554
- type CorrelatedResponsePayload<TType extends CorrelatedResponseType> = Extract<CorrelatedResponseMessage, {
555
- type: TType;
556
- }>["payload"];
568
+ type CorrelatedResponsePayloads = {
569
+ [Message in CorrelatedResponseMessage as Message["type"]]: Message["payload"];
570
+ };
571
+ type CorrelatedResponsePayload<TType extends CorrelatedResponseType> = CorrelatedResponsePayloads[TType];
572
+ export declare class DaemonConnectionError extends Error {
573
+ readonly code: "DAEMON_CONNECTION_LOST" | "DAEMON_REQUEST_TIMEOUT";
574
+ constructor(message: string, code?: "DAEMON_CONNECTION_LOST" | "DAEMON_REQUEST_TIMEOUT");
575
+ }
557
576
  export declare class DaemonClient {
558
577
  private config;
559
578
  private readonly providerSnapshotUpdates;
560
- private readonly subscriptions;
579
+ private readonly owned;
561
580
  private transport;
562
581
  private transportCleanup;
563
582
  private rawMessageListeners;
@@ -576,9 +595,6 @@ export declare class DaemonClient {
576
595
  private connectReject;
577
596
  private lastErrorValue;
578
597
  private connectionState;
579
- private checkoutDiffSubscriptions;
580
- private terminalDirectorySubscriptions;
581
- private fileSubscriptions;
582
598
  private readonly terminalStreams;
583
599
  private pendingBinaryFileReads;
584
600
  private activeBinaryFileTransfers;
@@ -593,6 +609,7 @@ export declare class DaemonClient {
593
609
  private runtimeMetricsInterval;
594
610
  private runtimeMetrics;
595
611
  private pingProbe;
612
+ private connectionVerification;
596
613
  private livenessHeartbeatTimer;
597
614
  private lastLivenessRttMs;
598
615
  private consecutiveLivenessFailures;
@@ -602,7 +619,10 @@ export declare class DaemonClient {
602
619
  private resolveConnect;
603
620
  private rejectConnect;
604
621
  close(): Promise<void>;
605
- ensureConnected(): void;
622
+ ensureConnected(options?: {
623
+ verify?: boolean;
624
+ }): void;
625
+ private verifyConnection;
606
626
  getConnectionState(): ConnectionState;
607
627
  subscribeConnectionStatus(listener: (status: ConnectionState) => void): () => void;
608
628
  get isConnected(): boolean;
@@ -679,18 +699,42 @@ export declare class DaemonClient {
679
699
  private startLivenessHeartbeat;
680
700
  private stopLivenessHeartbeat;
681
701
  private scheduleNextLivenessHeartbeat;
702
+ private observe;
703
+ registerBrowserHost(registration: Omit<Extract<SessionInboundMessage, {
704
+ type: "browser.host.register.request";
705
+ }>, "type" | "requestId">, options?: {
706
+ signal?: AbortSignal;
707
+ }): OwnedSubscription<CorrelatedResponsePayload<"browser.host.register.response">>;
708
+ observeEvents(events: SessionEventSubscription[], options?: {
709
+ signal?: AbortSignal;
710
+ notifications?: boolean;
711
+ }): OwnedSubscription<CorrelatedResponsePayload<"session.events.set_subscription.response">>;
712
+ observeAgents(options?: Omit<FetchAgentsOptions, "subscribe">): OwnedSubscription<FetchAgentsPayload>;
713
+ observeWorkspaces(options?: Omit<FetchWorkspacesOptions, "subscribe">): OwnedSubscription<FetchWorkspacesPayload>;
714
+ fetchAgents(options: FetchAgentsOptions & {
715
+ subscribe: {};
716
+ }): Promise<FetchAgentsPayload & {
717
+ subscription: OwnedSubscription<FetchAgentsPayload>;
718
+ }>;
682
719
  fetchAgents(options?: FetchAgentsOptions): Promise<FetchAgentsPayload>;
683
720
  fetchAgentHistory(options?: FetchAgentHistoryOptions): Promise<FetchAgentHistoryPayload>;
684
721
  fetchRecentProviderSessions(options?: FetchRecentProviderSessionsOptions): Promise<FetchRecentProviderSessionsPayload>;
722
+ fetchWorkspaces(options: FetchWorkspacesOptions & {
723
+ subscribe: {};
724
+ }): Promise<FetchWorkspacesPayload & {
725
+ subscription: OwnedSubscription<FetchWorkspacesPayload>;
726
+ }>;
685
727
  fetchWorkspaces(options?: FetchWorkspacesOptions): Promise<FetchWorkspacesPayload>;
686
- listWorkspaceLabels(options: {
687
- subscriptionId: string;
728
+ listWorkspaceLabels(options?: {
688
729
  sync?: {
689
730
  generation: string;
690
731
  afterSeq: number;
691
732
  };
692
733
  requestId?: string;
693
734
  }): Promise<WorkspaceLabelListPayload>;
735
+ observeWorkspaceLabels(options?: {
736
+ signal?: AbortSignal;
737
+ }): OwnedSubscription<WorkspaceLabelListPayload>;
694
738
  setWorkspaceLabel(options: {
695
739
  workspaceId: string;
696
740
  label: Extract<SessionInboundMessage, {
@@ -751,13 +795,9 @@ export declare class DaemonClient {
751
795
  fetchAgent(options: FetchAgentOptions): Promise<FetchAgentResult | null>;
752
796
  fetchAgent(agentId: string, requestId?: string): Promise<FetchAgentResult | null>;
753
797
  fetchAgent(agentId: string, options?: LegacyFetchAgentOptions): Promise<FetchAgentResult | null>;
754
- private resubscribeCheckoutDiffSubscriptions;
755
- private resubscribeTerminalDirectorySubscriptions;
756
- private resubscribeFileSubscriptions;
757
798
  private readonly creations;
758
799
  createAgent(options: CreateAgentRequestOptions): Promise<AgentSnapshotPayload>;
759
800
  private createLegacyAgent;
760
- private requireAgentRequestReceipts;
761
801
  deleteAgent(agentId: string): Promise<void>;
762
802
  archiveAgent(agentId: string): Promise<{
763
803
  archivedAt: string;
@@ -790,6 +830,7 @@ export declare class DaemonClient {
790
830
  seq: number;
791
831
  epoch: string;
792
832
  }>;
833
+ searchAgentTimeline({ agentId, query, cursor, }: AgentTimelineSearchOptions): Promise<AgentTimelineSearchPayload>;
793
834
  listAgentTimelinePrompts(agentId: string, options?: {
794
835
  requestId?: string;
795
836
  timeout?: number;
@@ -799,6 +840,9 @@ export declare class DaemonClient {
799
840
  timeout?: number;
800
841
  }): Promise<ProviderSubagentListPayload>;
801
842
  fetchProviderSubagentTimeline(parentAgentId: string, subagentId: string, options?: FetchProviderSubagentTimelineOptions): Promise<ProviderSubagentTimelinePayload>;
843
+ observeTimeline(agentIds: string[], options?: {
844
+ signal?: AbortSignal;
845
+ }): OwnedSubscription<CorrelatedResponsePayload<"agent.timeline.set_subscription.response">>;
802
846
  askSideConversation(parentAgentId: string, threadId: string, question: string, options?: {
803
847
  requestId?: string;
804
848
  timeout?: number;
@@ -811,13 +855,7 @@ export declare class DaemonClient {
811
855
  requestId?: string;
812
856
  timeout?: number;
813
857
  }): Promise<SideConversationListPayload>;
814
- setAgentTimelineSubscription(agentIds: string[]): Promise<void>;
815
- subscribeAgentTimeline(agentId: string, handler: (message: Extract<SessionOutboundMessage, {
816
- type: "agent_stream" | "agent.timeline.replacement";
817
- }>) => void): TimelineSubscription;
818
- private updateEventSubscriptions;
819
- private sendEventSubscription;
820
- private sendTimelineSubscription;
858
+ subscribeAgentTimeline(agentId: string, handler: (message: TimelineMessage) => void): TimelineSubscription;
821
859
  buildAgentForkContext(agentId: string, options?: AgentForkContextOptions): Promise<AgentForkContextPayload>;
822
860
  sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
823
861
  sendMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
@@ -835,7 +873,9 @@ export declare class DaemonClient {
835
873
  * Gated on `server_info.features.agentConfigApply`.
836
874
  */
837
875
  applyAgentConfig(agentId: string, config: AgentConfigApply): Promise<AgentProviderNotice | null>;
838
- restartServer(reason?: string, requestId?: string): Promise<RestartRequestedStatusPayload>;
876
+ restartServer(reason?: string, requestId?: string, options?: {
877
+ timeout?: number;
878
+ }): Promise<RestartRequestedStatusPayload>;
839
879
  shutdownServer(options?: ShutdownServerOptions): Promise<ShutdownRequestedStatusPayload>;
840
880
  updateDaemon(requestId?: string): Promise<DaemonUpdateResponse["payload"]>;
841
881
  setVoiceMode(enabled: boolean, agentId?: string): Promise<SetVoiceModePayload>;
@@ -858,15 +898,14 @@ export declare class DaemonClient {
858
898
  baseRef?: string;
859
899
  ignoreWhitespace?: boolean;
860
900
  }, requestId?: string): Promise<CheckoutDiffPayload>;
861
- subscribeCheckoutDiff(cwd: string, compare: {
901
+ observeCheckoutDiff(cwd: string, compare: {
862
902
  mode: "uncommitted" | "base";
863
903
  baseRef?: string;
864
904
  ignoreWhitespace?: boolean;
865
905
  }, options?: {
866
- subscriptionId?: string;
867
906
  requestId?: string;
868
- }): Promise<SubscribeCheckoutDiffPayload>;
869
- unsubscribeCheckoutDiff(subscriptionId: string): void;
907
+ signal?: AbortSignal;
908
+ }): OwnedSubscription<SubscribeCheckoutDiffPayload>;
870
909
  checkoutCommit(cwd: string, input: {
871
910
  message?: string;
872
911
  addAll?: boolean;
@@ -987,12 +1026,19 @@ export declare class DaemonClient {
987
1026
  private requestFileExplorer;
988
1027
  listDirectory(cwd: string, path: string, requestId?: string): Promise<FileExplorerDirectoryPayload>;
989
1028
  readFile(cwd: string, path: string, requestId?: string, maxBytes?: number): Promise<FileReadResult>;
1029
+ observeFile(input: {
1030
+ cwd: string;
1031
+ path: string;
1032
+ signal?: AbortSignal;
1033
+ }): OwnedSubscription<CorrelatedResponsePayload<"fs.file.subscribe.response">>;
990
1034
  subscribeFile(input: {
991
1035
  cwd: string;
992
1036
  path: string;
1037
+ signal?: AbortSignal;
993
1038
  }, onUpdate: (version: FileVersion) => void): Promise<{
994
1039
  initial: FileVersion;
995
- unsubscribe: () => void;
1040
+ subscription: OwnedSubscription<CorrelatedResponsePayload<"fs.file.subscribe.response">>;
1041
+ unsubscribe: () => Promise<void>;
996
1042
  }>;
997
1043
  writeFile(input: {
998
1044
  cwd: string;
@@ -1056,7 +1102,7 @@ export declare class DaemonClient {
1056
1102
  connectHub(hubUrl: string, token: string, permissions?: readonly string[], requestId?: string): Promise<{
1057
1103
  requestId: string;
1058
1104
  status: {
1059
- state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
1105
+ state: "not_connected" | "connecting" | "connected" | "reconnecting" | "disconnecting" | "revoked";
1060
1106
  daemonId: string | null;
1061
1107
  hubOrigin: string | null;
1062
1108
  permissions: ("daemon.read" | "daemon.manage" | "tunnel.manage" | "access.manage" | "workspace.read" | "workspace.write" | "workspace.manage" | "automation.manage" | "hub.execute")[];
@@ -1070,7 +1116,7 @@ export declare class DaemonClient {
1070
1116
  }, requestId?: string): Promise<{
1071
1117
  requestId: string;
1072
1118
  status: {
1073
- state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
1119
+ state: "not_connected" | "connecting" | "connected" | "reconnecting" | "disconnecting" | "revoked";
1074
1120
  daemonId: string | null;
1075
1121
  hubOrigin: string | null;
1076
1122
  permissions: ("daemon.read" | "daemon.manage" | "tunnel.manage" | "access.manage" | "workspace.read" | "workspace.write" | "workspace.manage" | "automation.manage" | "hub.execute")[];
@@ -1081,7 +1127,7 @@ export declare class DaemonClient {
1081
1127
  getHubStatus(requestId?: string): Promise<{
1082
1128
  requestId: string;
1083
1129
  status: {
1084
- state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
1130
+ state: "not_connected" | "connecting" | "connected" | "reconnecting" | "disconnecting" | "revoked";
1085
1131
  daemonId: string | null;
1086
1132
  hubOrigin: string | null;
1087
1133
  permissions: ("daemon.read" | "daemon.manage" | "tunnel.manage" | "access.manage" | "workspace.read" | "workspace.write" | "workspace.manage" | "automation.manage" | "hub.execute")[];
@@ -1092,7 +1138,7 @@ export declare class DaemonClient {
1092
1138
  disconnectHub(force?: boolean, requestId?: string): Promise<{
1093
1139
  requestId: string;
1094
1140
  status: {
1095
- state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
1141
+ state: "not_connected" | "connecting" | "connected" | "reconnecting" | "disconnecting" | "revoked";
1096
1142
  daemonId: string | null;
1097
1143
  hubOrigin: string | null;
1098
1144
  permissions: ("daemon.read" | "daemon.manage" | "tunnel.manage" | "access.manage" | "workspace.read" | "workspace.write" | "workspace.manage" | "automation.manage" | "hub.execute")[];
@@ -1149,6 +1195,12 @@ export declare class DaemonClient {
1149
1195
  ref?: string;
1150
1196
  }): Promise<PluginListItem>;
1151
1197
  getPluginSourceStatus(pluginId?: string): Promise<PluginSourceStatusItem[]>;
1198
+ private requirePluginUpdates;
1199
+ previewPluginUpdates(input?: {
1200
+ pluginId?: string;
1201
+ target?: PluginUpdateSelection;
1202
+ }): Promise<PluginUpdatePreview[]>;
1203
+ applyPluginUpdates(proposals: PluginUpdateProposal[]): Promise<PluginUpdateResult[]>;
1152
1204
  updatePluginSources(pluginId?: string): Promise<PluginSourceUpdateItem[]>;
1153
1205
  inspectDirectoryPlugin(path: string): Promise<{
1154
1206
  id: string;
@@ -1162,14 +1214,11 @@ export declare class DaemonClient {
1162
1214
  respondToPermissionAndWait(agentId: string, requestId: string, response: AgentPermissionResponse, timeout?: number): Promise<AgentPermissionResolvedPayload>;
1163
1215
  waitForAgentUpsert(agentId: string, predicate: (snapshot: AgentSnapshotPayload) => boolean, timeout?: number): Promise<AgentSnapshotPayload>;
1164
1216
  waitForFinish(agentId: string, timeout?: number): Promise<WaitForFinishResult>;
1165
- subscribeTerminals(input: {
1217
+ observeTerminals(input: {
1166
1218
  cwd: string;
1167
1219
  workspaceId?: string;
1168
- }): void;
1169
- unsubscribeTerminals(input: {
1170
- cwd: string;
1171
- workspaceId?: string;
1172
- }): void;
1220
+ signal?: AbortSignal;
1221
+ }): OwnedSubscription<CorrelatedResponsePayload<"terminals_changed">>;
1173
1222
  listTerminals(cwd?: string, requestId?: string, options?: {
1174
1223
  workspaceId?: string;
1175
1224
  }): Promise<ListTerminalsPayload>;
@@ -1184,11 +1233,23 @@ export declare class DaemonClient {
1184
1233
  };
1185
1234
  }): Promise<CreateTerminalPayload>;
1186
1235
  renameTerminal(input: RenameTerminalInput): Promise<RenameTerminalResult>;
1187
- subscribeTerminal(terminalId: string, optionsOrRequestId?: {
1236
+ /**
1237
+ * Snapshot/restore and output arrive in receive. Subscribe to the returned handle
1238
+ * for terminal_stream_exit: payload.error means observation failure, not PTY exit.
1239
+ * Either outcome detaches this slot and releases the handle automatically.
1240
+ */
1241
+ observeTerminal(terminalId: string, receive: (event: TerminalStreamEvent) => void, options?: {
1188
1242
  restore?: SubscribeTerminalRequest["restore"];
1243
+ signal?: AbortSignal;
1189
1244
  requestId?: string;
1190
- } | string): Promise<SubscribeTerminalPayload>;
1191
- unsubscribeTerminal(terminalId: string): void;
1245
+ }): OwnedSubscription<SubscribeTerminalPayload>;
1246
+ subscribeTerminal(terminalId: string, options?: {
1247
+ restore?: SubscribeTerminalRequest["restore"];
1248
+ signal?: AbortSignal;
1249
+ requestId?: string;
1250
+ }): Promise<SubscribeTerminalPayload & {
1251
+ subscription: OwnedSubscription<SubscribeTerminalPayload>;
1252
+ }>;
1192
1253
  sendTerminalInput(terminalId: string, message: TerminalInput["message"]): void;
1193
1254
  killTerminal(terminalId: string, requestId?: string): Promise<KillTerminalPayload>;
1194
1255
  closeItems(input: {