@getpaseo/client 0.1.110 → 0.2.0-beta.2

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.
@@ -1,7 +1,8 @@
1
1
  import type { z } from "zod";
2
2
  import { type ClientCapability } from "@getpaseo/protocol/client-capabilities";
3
+ import type { AgentAttentionNotificationPayload } from "@getpaseo/protocol/agent-attention-notification";
3
4
  import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ServerInfoStatusPayload } from "@getpaseo/protocol/messages";
4
- import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreatePaseoWorktreeRequest, FileDownloadTokenResponse, FileUploadResponse, FileExplorerResponse, FetchAgentTimelineResponseMessage, AgentForkContextResponseMessage, GitSetupOptions, CheckoutStatusResponse, CheckoutCommitResponse, CheckoutMergeResponse, CheckoutMergeFromBaseResponse, CheckoutPullResponse, CheckoutPushResponse, CheckoutRefreshResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutGithubSetAutoMergeResponse, CheckoutGithubGetCheckDetailsResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, ValidateBranchResponse, BranchSuggestionsResponse, GitHubSearchResponse, GitHubSearchRequest, DirectorySuggestionsResponse, PaseoWorktreeListResponse, PaseoWorktreeArchiveResponse, ProjectIconResponse, ProjectAddResponse, ProjectCreateDirectoryResponse, OpenProjectResponseMessage, WorkspaceGithubSearchRepositoriesResponse, ProjectGithubCloneProtocol, ProjectGithubCloneResponse, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest } from "@getpaseo/protocol/messages";
5
+ 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, ProjectIconResponse, ProjectAddResponse, ProjectCreateDirectoryResponse, OpenProjectResponseMessage, WorkspaceGithubSearchRepositoriesResponse, ProjectGithubCloneProtocol, ProjectGithubCloneResponse, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest, WorkspaceRecoveryState } from "@getpaseo/protocol/messages";
5
6
  import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@getpaseo/protocol/agent-types";
6
7
  import type { MutableDaemonConfig, MutableDaemonConfigPatch } from "@getpaseo/protocol/messages";
7
8
  import { type DaemonTransportFactory, type WebSocketFactory } from "./daemon-client-transport.js";
@@ -15,6 +16,7 @@ export interface Logger {
15
16
  }
16
17
  interface ImportAgentInputBase {
17
18
  cwd?: string;
19
+ workspaceId?: string;
18
20
  labels?: Record<string, string>;
19
21
  }
20
22
  export type ImportAgentInput = (ImportAgentInputBase & {
@@ -51,6 +53,11 @@ export type DaemonEvent = {
51
53
  payload: Extract<SessionOutboundMessage, {
52
54
  type: "workspace_update";
53
55
  }>["payload"];
56
+ } | {
57
+ type: "project.update";
58
+ payload: Extract<SessionOutboundMessage, {
59
+ type: "project.update";
60
+ }>["payload"];
54
61
  } | {
55
62
  type: "workspace_setup_progress";
56
63
  workspaceId: string;
@@ -127,6 +134,13 @@ export interface SendMessageOptions {
127
134
  }>;
128
135
  attachments?: SendAgentMessageRequest["attachments"];
129
136
  }
137
+ export interface AgentAttentionRequiredNotification {
138
+ agentId: string;
139
+ reason: "finished" | "error" | "permission";
140
+ timestamp: string;
141
+ shouldNotify: boolean;
142
+ notification?: AgentAttentionNotificationPayload;
143
+ }
130
144
  type AgentConfigOverrides = Partial<Omit<AgentSessionConfig, "provider" | "cwd">>;
131
145
  export interface CreateAgentRequestOptions extends AgentConfigOverrides {
132
146
  config?: AgentSessionConfig;
@@ -134,6 +148,7 @@ export interface CreateAgentRequestOptions extends AgentConfigOverrides {
134
148
  cwd?: string;
135
149
  env?: CreateAgentRequestMessage["env"];
136
150
  workspaceId?: string;
151
+ callerAgentId?: string;
137
152
  initialPrompt?: string;
138
153
  clientMessageId?: string;
139
154
  outputSchema?: Record<string, unknown>;
@@ -146,7 +161,7 @@ export interface CreateAgentRequestOptions extends AgentConfigOverrides {
146
161
  requestId?: string;
147
162
  labels?: Record<string, string>;
148
163
  }
149
- export interface CreatePaseoWorktreeInput extends Pick<CreatePaseoWorktreeRequest, "cwd" | "projectId" | "worktreeSlug" | "firstAgentContext" | "refName" | "action" | "githubPrNumber"> {
164
+ export interface CreatePaseoWorktreeInput extends Pick<CreatePaseoWorktreeRequest, "cwd" | "projectId" | "worktreeSlug" | "firstAgentContext" | "refName" | "action" | "checkoutSource" | "githubPrNumber"> {
150
165
  }
151
166
  type CheckoutStatusPayload = CheckoutStatusResponse["payload"];
152
167
  type SubscribeCheckoutDiffPayload = Extract<SessionOutboundMessage, {
@@ -161,7 +176,9 @@ type CheckoutPushPayload = CheckoutPushResponse["payload"];
161
176
  type CheckoutRefreshPayload = CheckoutRefreshResponse["payload"];
162
177
  type CheckoutPrCreatePayload = CheckoutPrCreateResponse["payload"];
163
178
  type CheckoutPrMergePayload = CheckoutPrMergeResponse["payload"];
179
+ type CheckoutForgeSetAutoMergePayload = CheckoutForgeSetAutoMergeResponse["payload"];
164
180
  type CheckoutGithubSetAutoMergePayload = CheckoutGithubSetAutoMergeResponse["payload"];
181
+ type CheckoutForgeGetCheckDetailsPayload = CheckoutForgeGetCheckDetailsResponse["payload"];
165
182
  type CheckoutGithubGetCheckDetailsPayload = CheckoutGithubGetCheckDetailsResponse["payload"];
166
183
  type CheckoutPrStatusPayload = CheckoutPrStatusResponse["payload"];
167
184
  type PullRequestTimelinePayload = PullRequestTimelineResponse["payload"];
@@ -172,6 +189,7 @@ type StashPopPayload = StashPopResponse["payload"];
172
189
  type StashListPayload = StashListResponse["payload"];
173
190
  type ValidateBranchPayload = ValidateBranchResponse["payload"];
174
191
  type BranchSuggestionsPayload = BranchSuggestionsResponse["payload"];
192
+ type ForgeSearchPayload = ForgeSearchResponse["payload"];
175
193
  type GitHubSearchPayload = GitHubSearchResponse["payload"];
176
194
  type DirectorySuggestionsPayload = DirectorySuggestionsResponse["payload"];
177
195
  type PaseoWorktreeListPayload = PaseoWorktreeListResponse["payload"];
@@ -192,6 +210,7 @@ export interface FileReadResult {
192
210
  path: string;
193
211
  kind: LegacyFileExplorerFilePayload["kind"];
194
212
  modifiedAt: string;
213
+ revision?: string;
195
214
  }
196
215
  export interface FileUploadInput {
197
216
  fileName: string;
@@ -473,9 +492,6 @@ export interface CreateScheduleOptions {
473
492
  prompt: string;
474
493
  name?: string | null;
475
494
  cadence: {
476
- type: "every";
477
- everyMs: number;
478
- } | {
479
495
  type: "cron";
480
496
  expression: string;
481
497
  timezone?: string;
@@ -529,9 +545,6 @@ export interface UpdateScheduleOptions {
529
545
  name?: string | null;
530
546
  prompt?: string;
531
547
  cadence?: {
532
- type: "every";
533
- everyMs: number;
534
- } | {
535
548
  type: "cron";
536
549
  expression: string;
537
550
  timezone?: string;
@@ -590,6 +603,7 @@ export declare class DaemonClient {
590
603
  private connectionState;
591
604
  private checkoutDiffSubscriptions;
592
605
  private terminalDirectorySubscriptions;
606
+ private fileSubscriptions;
593
607
  private readonly terminalStreams;
594
608
  private pendingBinaryFileReads;
595
609
  private activeBinaryFileTransfers;
@@ -626,6 +640,7 @@ export declare class DaemonClient {
626
640
  type: TType;
627
641
  }>) => void): () => void;
628
642
  on(handler: DaemonEventHandler): () => void;
643
+ onAgentAttentionRequired(handler: (notification: AgentAttentionRequiredNotification) => void): () => void;
629
644
  /**
630
645
  * Send a session message. For fire-and-forget messages (heartbeats, etc.),
631
646
  * failures are suppressed if `suppressSendErrors` is configured.
@@ -711,6 +726,7 @@ export declare class DaemonClient {
711
726
  fetchAgent(agentId: string, options?: LegacyFetchAgentOptions): Promise<FetchAgentResult | null>;
712
727
  private resubscribeCheckoutDiffSubscriptions;
713
728
  private resubscribeTerminalDirectorySubscriptions;
729
+ private resubscribeFileSubscriptions;
714
730
  createAgent(options: CreateAgentRequestOptions): Promise<AgentSnapshotPayload>;
715
731
  deleteAgent(agentId: string): Promise<void>;
716
732
  archiveAgent(agentId: string): Promise<{
@@ -733,6 +749,8 @@ export declare class DaemonClient {
733
749
  setWorkspacePinned(workspaceId: string, pinned: boolean, requestId?: string): Promise<{
734
750
  pinnedAt: string | null;
735
751
  }>;
752
+ inspectWorkspaceRecovery(workspaceId: string, requestId?: string): Promise<WorkspaceRecoveryState>;
753
+ restoreWorkspace(workspaceId: string, requestId?: string): Promise<void>;
736
754
  resumeAgent(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>): Promise<AgentSnapshotPayload>;
737
755
  importAgent(input: ImportAgentInput): Promise<AgentSnapshotPayload>;
738
756
  refreshAgent(agentId: string, requestId?: string): Promise<AgentRefreshedStatusPayload>;
@@ -742,6 +760,7 @@ export declare class DaemonClient {
742
760
  timeout?: number;
743
761
  }): Promise<ProviderSubagentListPayload>;
744
762
  fetchProviderSubagentTimeline(parentAgentId: string, subagentId: string, options?: FetchProviderSubagentTimelineOptions): Promise<ProviderSubagentTimelinePayload>;
763
+ setAgentTimelineSubscription(agentIds: string[]): Promise<void>;
745
764
  buildAgentForkContext(agentId: string, options?: AgentForkContextOptions): Promise<AgentForkContextPayload>;
746
765
  sendAgentMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
747
766
  sendMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
@@ -799,6 +818,13 @@ export declare class DaemonClient {
799
818
  checkoutPull(cwd: string, requestId?: string): Promise<CheckoutPullPayload>;
800
819
  checkoutPush(cwd: string, requestId?: string): Promise<CheckoutPushPayload>;
801
820
  checkoutRefresh(cwd: string, requestId?: string): Promise<CheckoutRefreshPayload>;
821
+ listCheckoutCommits(cwd: string, requestId?: string): Promise<{
822
+ baseRef: string | null;
823
+ commits: CheckoutCommit[];
824
+ }>;
825
+ getCommitFileDiff(cwd: string, sha: string, path: string, requestId?: string): Promise<{
826
+ file: ParsedDiffFile | null;
827
+ }>;
802
828
  checkoutPrCreate(cwd: string, input: {
803
829
  title?: string;
804
830
  body?: string;
@@ -807,17 +833,31 @@ export declare class DaemonClient {
807
833
  checkoutPrMerge(cwd: string, input: {
808
834
  method: CheckoutPrMergeMethod;
809
835
  }, requestId?: string): Promise<CheckoutPrMergePayload>;
836
+ checkoutForgeSetAutoMerge(cwd: string, input: {
837
+ enabled: true;
838
+ method: CheckoutPrMergeMethod;
839
+ } | {
840
+ enabled: false;
841
+ }, requestId?: string): Promise<CheckoutForgeSetAutoMergePayload>;
810
842
  checkoutGithubSetAutoMerge(cwd: string, input: {
811
843
  enabled: true;
812
844
  method: CheckoutPrMergeMethod;
813
845
  } | {
814
846
  enabled: false;
815
847
  }, requestId?: string): Promise<CheckoutGithubSetAutoMergePayload>;
848
+ checkoutForgeGetCheckDetails(input: {
849
+ cwd: string;
850
+ repoOwner?: string;
851
+ repoName?: string;
852
+ checkRunId?: number;
853
+ workflowRunId?: number;
854
+ changeRequestNumber?: number;
855
+ }, requestId?: string): Promise<CheckoutForgeGetCheckDetailsPayload>;
816
856
  checkoutGithubGetCheckDetails(input: {
817
857
  cwd: string;
818
- repoOwner: string;
819
- repoName: string;
820
- checkRunId: number;
858
+ repoOwner?: string;
859
+ repoName?: string;
860
+ checkRunId?: number;
821
861
  workflowRunId?: number;
822
862
  }, requestId?: string): Promise<CheckoutGithubGetCheckDetailsPayload>;
823
863
  checkoutPrStatus(cwd: string, requestId?: string): Promise<CheckoutPrStatusPayload>;
@@ -862,6 +902,12 @@ export declare class DaemonClient {
862
902
  query?: string;
863
903
  limit?: number;
864
904
  }, requestId?: string): Promise<BranchSuggestionsPayload>;
905
+ searchForge(options: {
906
+ cwd: string;
907
+ query: string;
908
+ limit?: number;
909
+ kinds?: ForgeSearchRequest["kinds"];
910
+ }, requestId?: string): Promise<ForgeSearchPayload>;
865
911
  searchGitHub(options: {
866
912
  cwd: string;
867
913
  query: string;
@@ -879,6 +925,20 @@ export declare class DaemonClient {
879
925
  private requestFileExplorer;
880
926
  listDirectory(cwd: string, path: string, requestId?: string): Promise<FileExplorerDirectoryPayload>;
881
927
  readFile(cwd: string, path: string, requestId?: string): Promise<FileReadResult>;
928
+ subscribeFile(input: {
929
+ cwd: string;
930
+ path: string;
931
+ }, onUpdate: (version: FileVersion) => void): Promise<{
932
+ initial: FileVersion;
933
+ unsubscribe: () => void;
934
+ }>;
935
+ writeFile(input: {
936
+ cwd: string;
937
+ path: string;
938
+ content: string;
939
+ expectedModifiedAt: string;
940
+ expectedRevision?: string;
941
+ }): Promise<FileWriteResult>;
882
942
  uploadFile(input: FileUploadInput): Promise<FileUploadResult>;
883
943
  requestDownloadToken(cwd: string, path: string, requestId?: string): Promise<FileDownloadTokenPayload>;
884
944
  requestProjectIcon(cwd: string, requestId?: string): Promise<ProjectIconResponse["payload"]>;
@@ -905,6 +965,40 @@ export declare class DaemonClient {
905
965
  config: MutableDaemonConfig;
906
966
  }>;
907
967
  getDaemonStatus(options?: DaemonStatusOptions): Promise<DaemonStatusPayload>;
968
+ connectHub(hubUrl: string, token: string, requestId?: string): Promise<{
969
+ requestId: string;
970
+ status: {
971
+ state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
972
+ daemonId: string | null;
973
+ hubOrigin: string | null;
974
+ scopes: string[];
975
+ connectedAt: string | null;
976
+ lastError: string | null;
977
+ };
978
+ }>;
979
+ getHubStatus(requestId?: string): Promise<{
980
+ requestId: string;
981
+ status: {
982
+ state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
983
+ daemonId: string | null;
984
+ hubOrigin: string | null;
985
+ scopes: string[];
986
+ connectedAt: string | null;
987
+ lastError: string | null;
988
+ };
989
+ }>;
990
+ disconnectHub(force?: boolean, requestId?: string): Promise<{
991
+ requestId: string;
992
+ status: {
993
+ state: "connecting" | "connected" | "not_connected" | "reconnecting" | "disconnecting" | "revoked";
994
+ daemonId: string | null;
995
+ hubOrigin: string | null;
996
+ scopes: string[];
997
+ connectedAt: string | null;
998
+ lastError: string | null;
999
+ };
1000
+ warning?: string | undefined;
1001
+ }>;
908
1002
  getDaemonPairingOffer(options?: DaemonPairingOfferOptions): Promise<DaemonPairingOfferPayload>;
909
1003
  collectDiagnostics(requestId?: string): Promise<DiagnosticsPayload>;
910
1004
  patchDaemonConfig(config: MutableDaemonConfigPatch, requestId?: string): Promise<{
@@ -995,6 +1089,7 @@ export declare class DaemonClient {
995
1089
  waitForTerminalStreamEvent(predicate: (event: TerminalStreamEvent) => boolean, timeout?: number): Promise<TerminalStreamEvent>;
996
1090
  private createRequestId;
997
1091
  getLastServerInfoMessage(): ServerInfoStatusPayload | null;
1092
+ private requireHubRelationshipSupport;
998
1093
  private resolveTransportUrlForAttempt;
999
1094
  private sendHelloMessage;
1000
1095
  private disposeTransport;
@@ -1015,6 +1110,7 @@ export declare class DaemonClient {
1015
1110
  private recordLivenessFailure;
1016
1111
  private handleSessionMessage;
1017
1112
  private resolveWaiters;
1113
+ private rejectWaitersForRequestId;
1018
1114
  private clearWaiters;
1019
1115
  private toEvent;
1020
1116
  private waitForWithCancel;
@@ -24,6 +24,34 @@ function normalizePassword(value) {
24
24
  }
25
25
  return value.length > 0 ? value : null;
26
26
  }
27
+ function extractCorrelatedResponseIdentity(input) {
28
+ if (!input || typeof input !== "object") {
29
+ return null;
30
+ }
31
+ const envelope = input;
32
+ if (envelope.type !== "session" || !envelope.message || typeof envelope.message !== "object") {
33
+ return null;
34
+ }
35
+ const message = envelope.message;
36
+ if (typeof message.type !== "string" ||
37
+ !(message.type === "rpc_error" ||
38
+ message.type.endsWith("_response") ||
39
+ message.type.endsWith(".response") ||
40
+ message.type.endsWith("/response"))) {
41
+ return null;
42
+ }
43
+ if (!message.payload || typeof message.payload !== "object") {
44
+ return null;
45
+ }
46
+ const payload = message.payload;
47
+ if (typeof payload.requestId !== "string") {
48
+ return null;
49
+ }
50
+ return {
51
+ requestId: payload.requestId,
52
+ responseType: message.type,
53
+ };
54
+ }
27
55
  // COMPAT(daemon-client-object-options): added in v0.1.102; remove after
28
56
  // 2026-12-29 once SDK callers have migrated to object parameters.
29
57
  function normalizeFetchAgentOptions(input, legacyOptions) {
@@ -58,6 +86,16 @@ class DaemonRpcError extends Error {
58
86
  this.code = params.code;
59
87
  }
60
88
  }
89
+ class DaemonProtocolError extends Error {
90
+ constructor(identity) {
91
+ const responseLabel = identity.responseType ?? "unknown response";
92
+ super(`Response validation failed for ${responseLabel}`);
93
+ this.code = "invalid_response";
94
+ this.name = "DaemonProtocolError";
95
+ this.requestId = identity.requestId;
96
+ this.responseType = identity.responseType;
97
+ }
98
+ }
61
99
  class PingTimeoutError extends Error {
62
100
  constructor(timeoutMs) {
63
101
  super(`Ping timed out (${timeoutMs}ms)`);
@@ -120,6 +158,7 @@ function legacyExplorerFileToBytes(file) {
120
158
  path: file.path,
121
159
  kind: file.kind,
122
160
  modifiedAt: file.modifiedAt,
161
+ revision: file.revision,
123
162
  };
124
163
  }
125
164
  function binaryFileKind(mime, encoding) {
@@ -192,6 +231,7 @@ export class DaemonClient {
192
231
  this.connectionState = { status: "idle" };
193
232
  this.checkoutDiffSubscriptions = new Map();
194
233
  this.terminalDirectorySubscriptions = new Map();
234
+ this.fileSubscriptions = new Map();
195
235
  this.terminalStreams = new TerminalStreamRouter();
196
236
  this.pendingBinaryFileReads = new Map();
197
237
  this.activeBinaryFileTransfers = new Map();
@@ -440,6 +480,7 @@ export class DaemonClient {
440
480
  this.rejectPendingSendQueue(new Error("Daemon client closed"));
441
481
  this.rejectPingProbe(new Error("Daemon client closed"));
442
482
  this.terminalStreams.clearSlots();
483
+ this.fileSubscriptions.clear();
443
484
  this.lastServerInfoMessage = null;
444
485
  if (this.runtimeMetricsInterval) {
445
486
  clearInterval(this.runtimeMetricsInterval);
@@ -518,6 +559,28 @@ export class DaemonClient {
518
559
  }
519
560
  };
520
561
  }
562
+ onAgentAttentionRequired(handler) {
563
+ const unsubscribeLegacy = this.on("agent_stream", (message) => {
564
+ if (message.payload.event.type !== "attention_required") {
565
+ return;
566
+ }
567
+ const event = message.payload.event;
568
+ handler({
569
+ agentId: message.payload.agentId,
570
+ reason: event.reason,
571
+ timestamp: event.timestamp,
572
+ shouldNotify: event.shouldNotify,
573
+ ...(event.notification ? { notification: event.notification } : {}),
574
+ });
575
+ });
576
+ const unsubscribeDedicated = this.on("agent_attention_required", (message) => {
577
+ handler(message.payload);
578
+ });
579
+ return () => {
580
+ unsubscribeLegacy();
581
+ unsubscribeDedicated();
582
+ };
583
+ }
521
584
  // ============================================================================
522
585
  // Core Send Helpers
523
586
  // ============================================================================
@@ -645,7 +708,7 @@ export class DaemonClient {
645
708
  return null;
646
709
  }
647
710
  return { kind: "ok", value };
648
- }, timeout, params.options);
711
+ }, timeout, { ...params.options, requestId: params.requestId });
649
712
  try {
650
713
  await this.sendSessionMessageOrThrow(params.message);
651
714
  }
@@ -1145,6 +1208,21 @@ export class DaemonClient {
1145
1208
  });
1146
1209
  }
1147
1210
  }
1211
+ resubscribeFileSubscriptions() {
1212
+ for (const [subscriptionId, subscription] of this.fileSubscriptions) {
1213
+ void this.sendCorrelatedSessionRequest({
1214
+ message: {
1215
+ type: "fs.file.subscribe.request",
1216
+ cwd: subscription.cwd,
1217
+ path: subscription.path,
1218
+ subscriptionId,
1219
+ },
1220
+ responseType: "fs.file.subscribe.response",
1221
+ })
1222
+ .then((payload) => subscription.onUpdate(payload.initial))
1223
+ .catch(() => undefined);
1224
+ }
1225
+ }
1148
1226
  // ============================================================================
1149
1227
  // Agent Lifecycle
1150
1228
  // ============================================================================
@@ -1157,6 +1235,7 @@ export class DaemonClient {
1157
1235
  config,
1158
1236
  ...(options.env ? { env: options.env } : {}),
1159
1237
  ...(options.workspaceId !== undefined ? { workspaceId: options.workspaceId } : {}),
1238
+ ...(options.callerAgentId !== undefined ? { callerAgentId: options.callerAgentId } : {}),
1160
1239
  ...(options.initialPrompt ? { initialPrompt: options.initialPrompt } : {}),
1161
1240
  ...(options.clientMessageId ? { clientMessageId: options.clientMessageId } : {}),
1162
1241
  ...(options.outputSchema ? { outputSchema: options.outputSchema } : {}),
@@ -1339,6 +1418,29 @@ export class DaemonClient {
1339
1418
  }
1340
1419
  return { pinnedAt: payload.pinnedAt };
1341
1420
  }
1421
+ async inspectWorkspaceRecovery(workspaceId, requestId) {
1422
+ const payload = await this.sendNamespacedCorrelatedSessionRequest({
1423
+ requestId,
1424
+ message: {
1425
+ type: "workspace.recovery.inspect.request",
1426
+ workspaceId,
1427
+ },
1428
+ });
1429
+ return payload.state;
1430
+ }
1431
+ async restoreWorkspace(workspaceId, requestId) {
1432
+ const payload = await this.sendNamespacedCorrelatedSessionRequest({
1433
+ requestId,
1434
+ message: {
1435
+ type: "workspace.recovery.restore.request",
1436
+ workspaceId,
1437
+ },
1438
+ timeout: 150000,
1439
+ });
1440
+ if (!payload.accepted) {
1441
+ throw new Error(payload.error ?? "Workspace recovery was rejected by the host");
1442
+ }
1443
+ }
1342
1444
  async resumeAgent(handle, overrides) {
1343
1445
  const requestId = this.createRequestId();
1344
1446
  const message = SessionInboundMessageSchema.parse({
@@ -1373,6 +1475,7 @@ export class DaemonClient {
1373
1475
  ? { providerId: input.providerId, providerHandleId: input.providerHandleId }
1374
1476
  : { provider: input.provider, sessionId: input.sessionId }),
1375
1477
  ...(input.cwd ? { cwd: input.cwd } : {}),
1478
+ ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),
1376
1479
  ...(input.labels && Object.keys(input.labels).length > 0 ? { labels: input.labels } : {}),
1377
1480
  });
1378
1481
  const status = await this.sendRequest({
@@ -1501,6 +1604,32 @@ export class DaemonClient {
1501
1604
  }
1502
1605
  return payload;
1503
1606
  }
1607
+ async setAgentTimelineSubscription(agentIds) {
1608
+ // COMPAT(selectiveAgentTimeline): added in v0.1.106. Old daemons keep their
1609
+ // legacy global stream and do not understand this RPC. Remove after
1610
+ // 2027-01-12 once the supported daemon floor is >= v0.1.106.
1611
+ if (!this.lastServerInfoMessage?.features?.selectiveAgentTimeline) {
1612
+ return;
1613
+ }
1614
+ const requestId = this.createRequestId();
1615
+ const normalizedAgentIds = [...new Set(agentIds)].sort();
1616
+ const message = SessionInboundMessageSchema.parse({
1617
+ type: "agent.timeline.set_subscription.request",
1618
+ agentIds: normalizedAgentIds,
1619
+ requestId,
1620
+ });
1621
+ await this.sendRequest({
1622
+ requestId,
1623
+ message,
1624
+ options: { skipQueue: true },
1625
+ select: (response) => {
1626
+ if (response.type !== "agent.timeline.set_subscription.response") {
1627
+ return null;
1628
+ }
1629
+ return response.payload.requestId === requestId ? response.payload : null;
1630
+ },
1631
+ });
1632
+ }
1504
1633
  async buildAgentForkContext(agentId, options = {}) {
1505
1634
  const resolvedRequestId = this.createRequestId(options.requestId);
1506
1635
  const message = SessionInboundMessageSchema.parse({
@@ -2197,6 +2326,36 @@ export class DaemonClient {
2197
2326
  responseType: "checkout.refresh.response",
2198
2327
  });
2199
2328
  }
2329
+ async listCheckoutCommits(cwd, requestId) {
2330
+ const payload = await this.sendNamespacedCorrelatedSessionRequest({
2331
+ requestId,
2332
+ message: {
2333
+ type: "checkout.commits.list.request",
2334
+ cwd,
2335
+ },
2336
+ timeout: 60000,
2337
+ });
2338
+ if (payload.error) {
2339
+ throw new Error(payload.error.message);
2340
+ }
2341
+ return { baseRef: payload.baseRef, commits: payload.commits };
2342
+ }
2343
+ async getCommitFileDiff(cwd, sha, path, requestId) {
2344
+ const payload = await this.sendNamespacedCorrelatedSessionRequest({
2345
+ requestId,
2346
+ message: {
2347
+ type: "checkout.commits.file_diff.request",
2348
+ cwd,
2349
+ sha,
2350
+ path,
2351
+ },
2352
+ timeout: 60000,
2353
+ });
2354
+ if (payload.error) {
2355
+ throw new Error(payload.error.message);
2356
+ }
2357
+ return { file: payload.file };
2358
+ }
2200
2359
  async checkoutPrCreate(cwd, input, requestId) {
2201
2360
  return this.sendCorrelatedSessionRequest({
2202
2361
  requestId,
@@ -2221,6 +2380,18 @@ export class DaemonClient {
2221
2380
  responseType: "checkout_pr_merge_response",
2222
2381
  });
2223
2382
  }
2383
+ async checkoutForgeSetAutoMerge(cwd, input, requestId) {
2384
+ return this.sendNamespacedCorrelatedSessionRequest({
2385
+ requestId,
2386
+ message: {
2387
+ type: "checkout.forge.set_auto_merge.request",
2388
+ cwd,
2389
+ enabled: input.enabled,
2390
+ ...(input.enabled ? { mergeMethod: input.method } : {}),
2391
+ },
2392
+ timeout: 60000,
2393
+ });
2394
+ }
2224
2395
  async checkoutGithubSetAutoMerge(cwd, input, requestId) {
2225
2396
  return this.sendNamespacedCorrelatedSessionRequest({
2226
2397
  requestId,
@@ -2232,6 +2403,21 @@ export class DaemonClient {
2232
2403
  },
2233
2404
  });
2234
2405
  }
2406
+ async checkoutForgeGetCheckDetails(input, requestId) {
2407
+ return this.sendNamespacedCorrelatedSessionRequest({
2408
+ requestId,
2409
+ message: {
2410
+ type: "checkout.forge.get_check_details.request",
2411
+ cwd: input.cwd,
2412
+ repoOwner: input.repoOwner,
2413
+ repoName: input.repoName,
2414
+ checkRunId: input.checkRunId,
2415
+ workflowRunId: input.workflowRunId,
2416
+ changeRequestNumber: input.changeRequestNumber,
2417
+ },
2418
+ timeout: 60000,
2419
+ });
2420
+ }
2235
2421
  async checkoutGithubGetCheckDetails(input, requestId) {
2236
2422
  return this.sendNamespacedCorrelatedSessionRequest({
2237
2423
  requestId,
@@ -2361,6 +2547,7 @@ export class DaemonClient {
2361
2547
  : {}),
2362
2548
  ...(input.refName !== undefined ? { refName: input.refName } : {}),
2363
2549
  ...(input.action !== undefined ? { action: input.action } : {}),
2550
+ ...(input.checkoutSource !== undefined ? { checkoutSource: input.checkoutSource } : {}),
2364
2551
  ...(input.githubPrNumber !== undefined ? { githubPrNumber: input.githubPrNumber } : {}),
2365
2552
  },
2366
2553
  responseType: "create_paseo_worktree_response",
@@ -2403,6 +2590,20 @@ export class DaemonClient {
2403
2590
  responseType: "branch_suggestions_response",
2404
2591
  });
2405
2592
  }
2593
+ async searchForge(options, requestId) {
2594
+ return this.sendCorrelatedSessionRequest({
2595
+ requestId,
2596
+ message: {
2597
+ type: "forge.search.request",
2598
+ cwd: options.cwd,
2599
+ query: options.query,
2600
+ limit: options.limit,
2601
+ kinds: options.kinds,
2602
+ },
2603
+ responseType: "forge.search.response",
2604
+ timeout: 15000,
2605
+ });
2606
+ }
2406
2607
  async searchGitHub(options, requestId) {
2407
2608
  return this.sendCorrelatedSessionRequest({
2408
2609
  requestId,
@@ -2482,6 +2683,43 @@ export class DaemonClient {
2482
2683
  this.activeBinaryFileTransfers.delete(resolvedRequestId);
2483
2684
  }
2484
2685
  }
2686
+ async subscribeFile(input, onUpdate) {
2687
+ const subscriptionId = this.createRequestId();
2688
+ this.fileSubscriptions.set(subscriptionId, { ...input, onUpdate });
2689
+ try {
2690
+ const payload = await this.sendCorrelatedSessionRequest({
2691
+ message: {
2692
+ type: "fs.file.subscribe.request",
2693
+ cwd: input.cwd,
2694
+ path: input.path,
2695
+ subscriptionId,
2696
+ },
2697
+ responseType: "fs.file.subscribe.response",
2698
+ });
2699
+ return {
2700
+ initial: payload.initial,
2701
+ unsubscribe: () => {
2702
+ if (!this.fileSubscriptions.delete(subscriptionId))
2703
+ return;
2704
+ void this.sendCorrelatedSessionRequest({
2705
+ message: { type: "fs.file.unsubscribe.request", subscriptionId },
2706
+ responseType: "fs.file.unsubscribe.response",
2707
+ }).catch(() => undefined);
2708
+ },
2709
+ };
2710
+ }
2711
+ catch (error) {
2712
+ this.fileSubscriptions.delete(subscriptionId);
2713
+ throw error;
2714
+ }
2715
+ }
2716
+ async writeFile(input) {
2717
+ const payload = await this.sendCorrelatedSessionRequest({
2718
+ message: { type: "fs.file.write.request", ...input },
2719
+ responseType: "fs.file.write.response",
2720
+ });
2721
+ return payload.result;
2722
+ }
2485
2723
  async uploadFile(input) {
2486
2724
  const bytes = asUint8Array(input.bytes);
2487
2725
  if (!bytes) {
@@ -2627,6 +2865,30 @@ export class DaemonClient {
2627
2865
  timeout: options?.timeout,
2628
2866
  });
2629
2867
  }
2868
+ async connectHub(hubUrl, token, requestId) {
2869
+ this.requireHubRelationshipSupport();
2870
+ return this.sendCorrelatedSessionRequest({
2871
+ requestId,
2872
+ message: { type: "hub.management.daemon.connect.request", hubUrl, token },
2873
+ responseType: "hub.management.daemon.connect.response",
2874
+ });
2875
+ }
2876
+ async getHubStatus(requestId) {
2877
+ this.requireHubRelationshipSupport();
2878
+ return this.sendCorrelatedSessionRequest({
2879
+ requestId,
2880
+ message: { type: "hub.management.daemon.get_status.request" },
2881
+ responseType: "hub.management.daemon.get_status.response",
2882
+ });
2883
+ }
2884
+ async disconnectHub(force = false, requestId) {
2885
+ this.requireHubRelationshipSupport();
2886
+ return this.sendCorrelatedSessionRequest({
2887
+ requestId,
2888
+ message: { type: "hub.management.daemon.disconnect.request", force },
2889
+ responseType: "hub.management.daemon.disconnect.response",
2890
+ });
2891
+ }
2630
2892
  async getDaemonPairingOffer(options) {
2631
2893
  return this.sendCorrelatedSessionRequest({
2632
2894
  requestId: options?.requestId,
@@ -3315,6 +3577,12 @@ export class DaemonClient {
3315
3577
  getLastServerInfoMessage() {
3316
3578
  return this.lastServerInfoMessage;
3317
3579
  }
3580
+ requireHubRelationshipSupport() {
3581
+ // COMPAT(hubRelationship): added in v0.1.X, drop the gate when floor >= v0.1.X.
3582
+ if (this.lastServerInfoMessage?.features?.hubRelationship !== true) {
3583
+ throw new Error("Update the host to use Hub relationship management.");
3584
+ }
3585
+ }
3318
3586
  resolveTransportUrlForAttempt() {
3319
3587
  return this.config.url;
3320
3588
  }
@@ -3338,6 +3606,7 @@ export class DaemonClient {
3338
3606
  [CLIENT_CAPS.reasoningMergeEnum]: true,
3339
3607
  [CLIENT_CAPS.terminalReflowableSnapshot]: true,
3340
3608
  [CLIENT_CAPS.providerSubagents]: true,
3609
+ [CLIENT_CAPS.projectUpdates]: true,
3341
3610
  ...this.config.capabilities,
3342
3611
  },
3343
3612
  ...(this.config.appVersion ? { appVersion: this.config.appVersion } : {}),
@@ -3427,13 +3696,18 @@ export class DaemonClient {
3427
3696
  }
3428
3697
  const parsed = validateWSOutboundMessage(parsedJson);
3429
3698
  if (!parsed.success) {
3430
- const msgType = parsedJson != null &&
3699
+ const responseIdentity = extractCorrelatedResponseIdentity(parsedJson);
3700
+ const envelopeType = parsedJson != null &&
3431
3701
  typeof parsedJson === "object" &&
3432
3702
  "type" in parsedJson &&
3433
3703
  typeof parsedJson.type === "string"
3434
3704
  ? parsedJson.type
3435
3705
  : "unknown";
3706
+ const msgType = responseIdentity?.responseType ?? envelopeType;
3436
3707
  this.logger.warn({ msgType, error: parsed.error.message }, "Message validation failed");
3708
+ if (responseIdentity) {
3709
+ this.rejectWaitersForRequestId(responseIdentity.requestId, new DaemonProtocolError(responseIdentity));
3710
+ }
3437
3711
  return;
3438
3712
  }
3439
3713
  this.consecutiveLivenessFailures = 0;
@@ -3489,6 +3763,7 @@ export class DaemonClient {
3489
3763
  size: frame.metadata.size,
3490
3764
  encoding: frame.metadata.encoding,
3491
3765
  modifiedAt: frame.metadata.modifiedAt,
3766
+ revision: frame.metadata.revision,
3492
3767
  chunks: [],
3493
3768
  });
3494
3769
  return;
@@ -3510,6 +3785,7 @@ export class DaemonClient {
3510
3785
  path: transfer.path,
3511
3786
  kind: binaryFileKind(transfer.mime, transfer.encoding),
3512
3787
  modifiedAt: transfer.modifiedAt,
3788
+ revision: transfer.revision,
3513
3789
  });
3514
3790
  this.handleSessionMessage({
3515
3791
  type: "file_explorer_response",
@@ -3658,6 +3934,7 @@ export class DaemonClient {
3658
3934
  this.startLivenessHeartbeat();
3659
3935
  this.resubscribeCheckoutDiffSubscriptions();
3660
3936
  this.resubscribeTerminalDirectorySubscriptions();
3937
+ this.resubscribeFileSubscriptions();
3661
3938
  this.flushPendingSendQueue();
3662
3939
  this.resolveConnect();
3663
3940
  }
@@ -3666,6 +3943,11 @@ export class DaemonClient {
3666
3943
  if (consumerMessage.type === "terminal_stream_exit") {
3667
3944
  this.terminalStreams.removeTerminal(consumerMessage.payload.terminalId);
3668
3945
  }
3946
+ if (consumerMessage.type === "fs.file.update") {
3947
+ this.fileSubscriptions
3948
+ .get(consumerMessage.payload.subscriptionId)
3949
+ ?.onUpdate(consumerMessage.payload.version);
3950
+ }
3669
3951
  if (this.rawMessageListeners.size > 0) {
3670
3952
  for (const handler of this.rawMessageListeners) {
3671
3953
  try {
@@ -3707,6 +3989,18 @@ export class DaemonClient {
3707
3989
  }
3708
3990
  }
3709
3991
  }
3992
+ rejectWaitersForRequestId(requestId, error) {
3993
+ for (const waiter of Array.from(this.waiters)) {
3994
+ if (waiter.requestId !== requestId) {
3995
+ continue;
3996
+ }
3997
+ this.waiters.delete(waiter);
3998
+ if (waiter.timeoutHandle) {
3999
+ clearTimeout(waiter.timeoutHandle);
4000
+ }
4001
+ waiter.reject(error);
4002
+ }
4003
+ }
3710
4004
  clearWaiters(error) {
3711
4005
  for (const waiter of Array.from(this.waiters)) {
3712
4006
  if (waiter.timeoutHandle) {
@@ -3730,6 +4024,8 @@ export class DaemonClient {
3730
4024
  workspaceId: msg.payload.kind === "upsert" ? msg.payload.workspace.id : msg.payload.id,
3731
4025
  payload: msg.payload,
3732
4026
  };
4027
+ case "project.update":
4028
+ return { type: "project.update", payload: msg.payload };
3733
4029
  case "workspace_setup_progress":
3734
4030
  return {
3735
4031
  type: "workspace_setup_progress",
@@ -3771,7 +4067,7 @@ export class DaemonClient {
3771
4067
  return null;
3772
4068
  }
3773
4069
  }
3774
- waitForWithCancel(predicate, timeout = 30000, _options) {
4070
+ waitForWithCancel(predicate, timeout = 30000, options) {
3775
4071
  // Capture stack trace at call site, not inside setTimeout
3776
4072
  const timeoutError = new Error(`Timeout waiting for message (${timeout}ms)`);
3777
4073
  let waiter = null;
@@ -3804,6 +4100,7 @@ export class DaemonClient {
3804
4100
  resolve: wrappedResolve,
3805
4101
  reject: wrappedReject,
3806
4102
  timeoutHandle,
4103
+ requestId: options?.requestId,
3807
4104
  };
3808
4105
  this.waiters.add(waiter);
3809
4106
  });
package/dist/index.d.ts CHANGED
@@ -118,13 +118,13 @@ export interface PaseoAgentCreateOptions extends PaseoAgentConfigOverrides {
118
118
  provider?: CreateAgentRequestMessage["config"]["provider"];
119
119
  cwd?: string;
120
120
  workspaceId?: string;
121
+ callerAgentId?: string;
121
122
  initialPrompt?: string;
122
123
  clientMessageId?: string;
123
124
  outputSchema?: Record<string, unknown>;
124
125
  images?: CreateAgentRequestMessage["images"];
125
126
  attachments?: CreateAgentRequestMessage["attachments"];
126
127
  git?: CreateAgentRequestMessage["git"];
127
- worktreeName?: string;
128
128
  requestId?: string;
129
129
  labels?: Record<string, string>;
130
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpaseo/client",
3
- "version": "0.1.110",
3
+ "version": "0.2.0-beta.2",
4
4
  "description": "Paseo client SDK package",
5
5
  "files": [
6
6
  "dist",
@@ -35,8 +35,8 @@
35
35
  "test": "vitest run"
36
36
  },
37
37
  "dependencies": {
38
- "@getpaseo/protocol": "0.1.110",
39
- "@getpaseo/relay": "0.1.110",
38
+ "@getpaseo/protocol": "0.2.0-beta.2",
39
+ "@getpaseo/relay": "0.2.0-beta.2",
40
40
  "zod": "^4.4.3"
41
41
  },
42
42
  "devDependencies": {