@getpaseo/client 0.1.97-beta.3 → 0.1.98

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,7 @@
1
1
  import type { z } from "zod";
2
2
  import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, type ServerInfoStatusPayload } from "@getpaseo/protocol/messages";
3
- import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreatePaseoWorktreeRequest, FileDownloadTokenResponse, FileUploadResponse, FileExplorerResponse, FetchAgentTimelineResponseMessage, 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, OpenProjectResponseMessage, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest } from "@getpaseo/protocol/messages";
4
- import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProvider, AgentSessionConfig } from "@getpaseo/protocol/agent-types";
3
+ import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreatePaseoWorktreeRequest, FileDownloadTokenResponse, FileUploadResponse, FileExplorerResponse, FetchAgentTimelineResponseMessage, 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, OpenProjectResponseMessage, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest } from "@getpaseo/protocol/messages";
4
+ import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@getpaseo/protocol/agent-types";
5
5
  import type { MutableDaemonConfig, MutableDaemonConfigPatch } from "@getpaseo/protocol/messages";
6
6
  import { type DaemonTransportFactory, type WebSocketFactory } from "./daemon-client-transport.js";
7
7
  import { type TerminalStreamEvent } from "./terminal-stream-router.js";
@@ -205,6 +205,7 @@ type ListAvailableProvidersPayload = ListAvailableProvidersResponse["payload"];
205
205
  type GetProvidersSnapshotPayload = GetProvidersSnapshotResponseMessage["payload"];
206
206
  type RefreshProvidersSnapshotPayload = RefreshProvidersSnapshotResponseMessage["payload"];
207
207
  type ProviderDiagnosticPayload = ProviderDiagnosticResponseMessage["payload"];
208
+ type ProviderUsageListPayload = ProviderUsageListResponseMessage["payload"];
208
209
  type DaemonStatusPayload = DaemonGetStatusResponse["payload"];
209
210
  type DaemonPairingOfferPayload = DaemonGetPairingOfferResponse["payload"];
210
211
  type ReadProjectConfigPayload = Extract<SessionOutboundMessage, {
@@ -497,6 +498,7 @@ export interface RenameTerminalInput {
497
498
  requestId?: string;
498
499
  }
499
500
  type OpenProjectPayload = OpenProjectResponseMessage["payload"];
501
+ type ProjectAddPayload = ProjectAddResponse["payload"];
500
502
  type ArchiveWorkspacePayload = ArchiveWorkspaceResponseMessage["payload"];
501
503
  type WorkspaceSetupStatusPayload = WorkspaceSetupStatusResponseMessage["payload"];
502
504
  export interface FetchAgentResult {
@@ -544,7 +546,9 @@ export declare class DaemonClient {
544
546
  private lastServerInfoMessage;
545
547
  private runtimeMetricsInterval;
546
548
  private runtimeMetrics;
547
- private livenessProbe;
549
+ private pingProbe;
550
+ private livenessHeartbeatTimer;
551
+ private lastLivenessRttMs;
548
552
  private consecutiveLivenessFailures;
549
553
  constructor(config: DaemonClientConfig);
550
554
  connect(): Promise<void>;
@@ -558,6 +562,7 @@ export declare class DaemonClient {
558
562
  get isConnected(): boolean;
559
563
  get isConnecting(): boolean;
560
564
  get lastError(): string | null;
565
+ getLastLivenessRttMs(): number | null;
561
566
  subscribe(handler: DaemonEventHandler): () => void;
562
567
  subscribeRawMessages(handler: (message: SessionOutboundMessage) => void): () => void;
563
568
  on<TType extends SessionOutboundMessage["type"]>(type: TType, handler: (message: Extract<SessionOutboundMessage, {
@@ -612,16 +617,20 @@ export declare class DaemonClient {
612
617
  serverSentAt: number;
613
618
  rttMs: number;
614
619
  }>;
615
- checkLiveness(params?: {
620
+ measureLatency(params?: {
616
621
  timeoutMs?: number;
617
- }): Promise<{
618
- rttMs: number;
619
- }>;
622
+ }): Promise<number>;
623
+ private livenessPing;
624
+ private sendPingAwaitRtt;
625
+ private startLivenessHeartbeat;
626
+ private stopLivenessHeartbeat;
627
+ private scheduleNextLivenessHeartbeat;
620
628
  fetchAgents(options?: FetchAgentsOptions): Promise<FetchAgentsPayload>;
621
629
  fetchAgentHistory(options?: FetchAgentHistoryOptions): Promise<FetchAgentHistoryPayload>;
622
630
  fetchRecentProviderSessions(options?: FetchRecentProviderSessionsOptions): Promise<FetchRecentProviderSessionsPayload>;
623
631
  fetchWorkspaces(options?: FetchWorkspacesOptions): Promise<FetchWorkspacesPayload>;
624
632
  openProject(cwd: string, requestId?: string): Promise<OpenProjectPayload>;
633
+ addProject(cwd: string, requestId?: string): Promise<ProjectAddPayload>;
625
634
  startWorkspaceScript(workspaceId: string, scriptName: string, requestId?: string): Promise<Extract<SessionOutboundMessage, {
626
635
  type: "start_workspace_script_response";
627
636
  }>["payload"]>;
@@ -635,6 +644,7 @@ export declare class DaemonClient {
635
644
  archiveAgent(agentId: string): Promise<{
636
645
  archivedAt: string;
637
646
  }>;
647
+ detachAgent(agentId: string): Promise<void>;
638
648
  updateAgent(agentId: string, updates: {
639
649
  name?: string;
640
650
  labels?: Record<string, string>;
@@ -642,6 +652,9 @@ export declare class DaemonClient {
642
652
  renameProject(projectId: string, customName: string | null, requestId?: string): Promise<{
643
653
  customName: string | null;
644
654
  }>;
655
+ removeProject(projectId: string, requestId?: string): Promise<{
656
+ removedWorkspaceIds: string[];
657
+ }>;
645
658
  setWorkspaceTitle(workspaceId: string, title: string | null, requestId?: string): Promise<{
646
659
  title: string | null;
647
660
  }>;
@@ -653,10 +666,10 @@ export declare class DaemonClient {
653
666
  sendMessage(agentId: string, text: string, options?: SendMessageOptions): Promise<void>;
654
667
  rewindAgent(agentId: string, messageId: string, mode: "conversation" | "files" | "both"): Promise<AgentRewindResponseMessage["payload"]>;
655
668
  cancelAgent(agentId: string): Promise<void>;
656
- setAgentMode(agentId: string, modeId: string): Promise<void>;
669
+ setAgentMode(agentId: string, modeId: string): Promise<AgentProviderNotice | null>;
657
670
  setAgentModel(agentId: string, modelId: string | null): Promise<void>;
658
671
  setAgentFeature(agentId: string, featureId: string, value: unknown): Promise<void>;
659
- setAgentThinkingOption(agentId: string, thinkingOptionId: string | null): Promise<void>;
672
+ setAgentThinkingOption(agentId: string, thinkingOptionId: string | null): Promise<AgentProviderNotice | null>;
660
673
  restartServer(reason?: string, requestId?: string): Promise<RestartRequestedStatusPayload>;
661
674
  shutdownServer(requestId?: string): Promise<ShutdownRequestedStatusPayload>;
662
675
  setVoiceMode(enabled: boolean, agentId?: string): Promise<SetVoiceModePayload>;
@@ -825,6 +838,9 @@ export declare class DaemonClient {
825
838
  getProviderDiagnostic(provider: AgentProvider, options?: {
826
839
  requestId?: string;
827
840
  }): Promise<ProviderDiagnosticPayload>;
841
+ listProviderUsage(options?: {
842
+ requestId?: string;
843
+ }): Promise<ProviderUsageListPayload>;
828
844
  listCommands(agentId: string, requestId?: string): Promise<ListCommandsPayload>;
829
845
  listCommands(agentId: string, options?: ListCommandsOptions): Promise<ListCommandsPayload>;
830
846
  respondToPermission(agentId: string, requestId: string, response: AgentPermissionResponse): Promise<void>;
@@ -904,9 +920,9 @@ export declare class DaemonClient {
904
920
  private scheduleReconnect;
905
921
  private emitDisconnectedStateForReconnect;
906
922
  private armReconnectTimer;
907
- private resolveLivenessProbe;
908
- private clearLivenessProbe;
909
- private rejectLivenessProbe;
923
+ private resolvePingProbe;
924
+ private clearPingProbe;
925
+ private rejectPingProbe;
910
926
  private recordLivenessFailure;
911
927
  private handleSessionMessage;
912
928
  private resolveWaiters;
@@ -35,10 +35,25 @@ class DaemonRpcError extends Error {
35
35
  this.code = params.code;
36
36
  }
37
37
  }
38
+ class PingTimeoutError extends Error {
39
+ constructor(timeoutMs) {
40
+ super(`Ping timed out (${timeoutMs}ms)`);
41
+ this.timeoutMs = timeoutMs;
42
+ this.name = "PingTimeoutError";
43
+ }
44
+ }
45
+ function toTimeoutError(error, label, timeoutMs) {
46
+ if (error instanceof PingTimeoutError) {
47
+ return new Error(`${label} timed out (${timeoutMs}ms)`);
48
+ }
49
+ return error instanceof Error ? error : new Error(String(error));
50
+ }
38
51
  const DEFAULT_RECONNECT_BASE_DELAY_MS = 1500;
39
52
  const DEFAULT_RECONNECT_MAX_DELAY_MS = 30000;
40
53
  const DEFAULT_CONNECT_TIMEOUT_MS = 15000;
41
54
  const DEFAULT_LIVENESS_TIMEOUT_MS = 5000;
55
+ const LIVENESS_HEARTBEAT_INTERVAL_MS = 10000;
56
+ const LIVENESS_HEARTBEAT_TIMEOUT_MS = 15000;
42
57
  const LIVENESS_FAILURE_RECONNECT_THRESHOLD = 2;
43
58
  /** Default timeout for waiting for connection before sending queued messages */
44
59
  const DEFAULT_SEND_QUEUE_TIMEOUT_MS = 10000;
@@ -161,7 +176,9 @@ export class DaemonClient {
161
176
  this.lastServerInfoMessage = null;
162
177
  this.runtimeMetricsInterval = null;
163
178
  this.runtimeMetrics = null;
164
- this.livenessProbe = null;
179
+ this.pingProbe = null;
180
+ this.livenessHeartbeatTimer = null;
181
+ this.lastLivenessRttMs = null;
165
182
  this.consecutiveLivenessFailures = 0;
166
183
  this.logger = config.logger ?? consoleLogger;
167
184
  this.logConnectionPath = isRelayClientWebSocketUrl(this.config.url) ? "relay" : "direct";
@@ -397,7 +414,7 @@ export class DaemonClient {
397
414
  this.disposeTransport(1000, "Client closed");
398
415
  this.clearWaiters(new Error("Daemon client closed"));
399
416
  this.rejectPendingSendQueue(new Error("Daemon client closed"));
400
- this.rejectLivenessProbe(new Error("Daemon client closed"));
417
+ this.rejectPingProbe(new Error("Daemon client closed"));
401
418
  this.terminalStreams.clearSlots();
402
419
  this.lastServerInfoMessage = null;
403
420
  if (this.runtimeMetricsInterval) {
@@ -440,6 +457,9 @@ export class DaemonClient {
440
457
  get lastError() {
441
458
  return this.lastErrorValue;
442
459
  }
460
+ getLastLivenessRttMs() {
461
+ return this.lastLivenessRttMs;
462
+ }
443
463
  // ============================================================================
444
464
  // Message Subscription
445
465
  // ============================================================================
@@ -765,15 +785,32 @@ export class DaemonClient {
765
785
  rttMs: Date.now() - clientSentAt,
766
786
  };
767
787
  }
768
- checkLiveness(params) {
788
+ measureLatency(params) {
789
+ const timeoutMs = Math.max(1, params?.timeoutMs ?? DEFAULT_LIVENESS_TIMEOUT_MS);
790
+ return this.sendPingAwaitRtt({ timeoutMs, drivesLivenessFailure: false }).catch((error) => {
791
+ throw toTimeoutError(error, "Latency measurement", timeoutMs);
792
+ });
793
+ }
794
+ async livenessPing(params) {
795
+ const timeoutMs = Math.max(1, params?.timeoutMs ?? DEFAULT_LIVENESS_TIMEOUT_MS);
796
+ try {
797
+ const rttMs = await this.sendPingAwaitRtt({ timeoutMs, drivesLivenessFailure: true });
798
+ this.lastLivenessRttMs = rttMs;
799
+ return rttMs;
800
+ }
801
+ catch (error) {
802
+ throw toTimeoutError(error, "Liveness check", timeoutMs);
803
+ }
804
+ }
805
+ sendPingAwaitRtt(params) {
769
806
  if (this.connectionState.status !== "connected" || !this.transport) {
770
807
  return Promise.reject(new Error(`Transport not connected (status: ${this.connectionState.status})`));
771
808
  }
772
- if (this.livenessProbe) {
773
- return this.livenessProbe.promise;
809
+ if (this.pingProbe) {
810
+ return this.pingProbe.promise;
774
811
  }
775
812
  const startedAt = perfNow();
776
- const timeoutMs = Math.max(1, params?.timeoutMs ?? DEFAULT_LIVENESS_TIMEOUT_MS);
813
+ const timeoutMs = params.timeoutMs;
777
814
  let resolveProbe = null;
778
815
  let rejectProbe = null;
779
816
  const promise = new Promise((resolve, reject) => {
@@ -785,28 +822,58 @@ export class DaemonClient {
785
822
  resolve: (value) => resolveProbe?.(value),
786
823
  reject: (error) => rejectProbe?.(error),
787
824
  timeoutHandle: setTimeout(() => {
788
- if (this.livenessProbe !== probe) {
825
+ if (this.pingProbe !== probe) {
789
826
  return;
790
827
  }
791
- this.livenessProbe = null;
792
- const error = new Error(`Liveness check timed out (${timeoutMs}ms)`);
828
+ this.pingProbe = null;
829
+ const error = new PingTimeoutError(timeoutMs);
793
830
  probe.reject(error);
794
- this.recordLivenessFailure(error);
831
+ if (probe.drivesLivenessFailure) {
832
+ this.recordLivenessFailure(toTimeoutError(error, "Liveness check", timeoutMs));
833
+ }
795
834
  }, timeoutMs),
796
835
  startedAt,
836
+ drivesLivenessFailure: params.drivesLivenessFailure,
797
837
  };
798
- this.livenessProbe = probe;
838
+ this.pingProbe = probe;
799
839
  try {
800
840
  this.transport.send(JSON.stringify({ type: "ping" }));
801
841
  }
802
842
  catch (error) {
803
- this.clearLivenessProbe();
804
- const err = error instanceof Error ? error : new Error(String(error));
805
- this.recordLivenessFailure(err);
806
- return Promise.reject(err);
843
+ this.clearPingProbe();
844
+ const sendError = error instanceof Error ? error : new Error(String(error));
845
+ if (probe.drivesLivenessFailure) {
846
+ this.recordLivenessFailure(sendError);
847
+ }
848
+ return Promise.reject(sendError);
807
849
  }
808
850
  return promise;
809
851
  }
852
+ startLivenessHeartbeat() {
853
+ this.stopLivenessHeartbeat();
854
+ this.lastLivenessRttMs = null;
855
+ this.scheduleNextLivenessHeartbeat();
856
+ }
857
+ stopLivenessHeartbeat() {
858
+ if (!this.livenessHeartbeatTimer) {
859
+ return;
860
+ }
861
+ clearTimeout(this.livenessHeartbeatTimer);
862
+ this.livenessHeartbeatTimer = null;
863
+ }
864
+ scheduleNextLivenessHeartbeat() {
865
+ if (this.connectionState.status !== "connected" || this.livenessHeartbeatTimer) {
866
+ return;
867
+ }
868
+ this.livenessHeartbeatTimer = setTimeout(() => {
869
+ this.livenessHeartbeatTimer = null;
870
+ this.livenessPing({ timeoutMs: LIVENESS_HEARTBEAT_TIMEOUT_MS })
871
+ .catch(() => { })
872
+ .finally(() => {
873
+ this.scheduleNextLivenessHeartbeat();
874
+ });
875
+ }, LIVENESS_HEARTBEAT_INTERVAL_MS);
876
+ }
810
877
  // ============================================================================
811
878
  // Agent RPCs (requestId-correlated)
812
879
  // ============================================================================
@@ -925,6 +992,17 @@ export class DaemonClient {
925
992
  timeout: 10000,
926
993
  });
927
994
  }
995
+ async addProject(cwd, requestId) {
996
+ return this.sendCorrelatedSessionRequest({
997
+ requestId,
998
+ message: {
999
+ type: "project.add.request",
1000
+ cwd,
1001
+ },
1002
+ responseType: "project.add.response",
1003
+ timeout: 10000,
1004
+ });
1005
+ }
928
1006
  async startWorkspaceScript(workspaceId, scriptName, requestId) {
929
1007
  return this.sendCorrelatedSessionRequest({
930
1008
  requestId,
@@ -1117,6 +1195,18 @@ export class DaemonClient {
1117
1195
  });
1118
1196
  return { archivedAt: result.archivedAt };
1119
1197
  }
1198
+ async detachAgent(agentId) {
1199
+ const payload = await this.sendNamespacedCorrelatedSessionRequest({
1200
+ message: {
1201
+ type: "agent.detach.request",
1202
+ agentId,
1203
+ },
1204
+ timeout: 10000,
1205
+ });
1206
+ if (!payload.accepted) {
1207
+ throw new Error(payload.error ?? "detachAgent rejected");
1208
+ }
1209
+ }
1120
1210
  async updateAgent(agentId, updates) {
1121
1211
  const requestId = this.createRequestId();
1122
1212
  const message = SessionInboundMessageSchema.parse({
@@ -1163,6 +1253,20 @@ export class DaemonClient {
1163
1253
  }
1164
1254
  return { customName: payload.customName };
1165
1255
  }
1256
+ async removeProject(projectId, requestId) {
1257
+ const payload = await this.sendNamespacedCorrelatedSessionRequest({
1258
+ requestId,
1259
+ message: {
1260
+ type: "project.remove.request",
1261
+ projectId,
1262
+ },
1263
+ timeout: 10000,
1264
+ });
1265
+ if (!payload.accepted) {
1266
+ throw new Error(payload.error ?? "removeProject rejected");
1267
+ }
1268
+ return { removedWorkspaceIds: payload.removedWorkspaceIds };
1269
+ }
1166
1270
  async setWorkspaceTitle(workspaceId, title, requestId) {
1167
1271
  const payload = await this.sendCorrelatedSessionRequest({
1168
1272
  requestId,
@@ -1411,6 +1515,7 @@ export class DaemonClient {
1411
1515
  if (!payload.accepted) {
1412
1516
  throw new Error(payload.error ?? "setAgentMode rejected");
1413
1517
  }
1518
+ return payload.notice ?? null;
1414
1519
  }
1415
1520
  async setAgentModel(agentId, modelId) {
1416
1521
  const requestId = this.createRequestId();
@@ -1493,6 +1598,7 @@ export class DaemonClient {
1493
1598
  if (!payload.accepted) {
1494
1599
  throw new Error(payload.error ?? "setAgentThinkingOption rejected");
1495
1600
  }
1601
+ return payload.notice ?? null;
1496
1602
  }
1497
1603
  async restartServer(reason, requestId) {
1498
1604
  const resolvedRequestId = this.createRequestId(requestId);
@@ -2472,6 +2578,15 @@ export class DaemonClient {
2472
2578
  timeout: 30000,
2473
2579
  });
2474
2580
  }
2581
+ async listProviderUsage(options) {
2582
+ return this.sendNamespacedCorrelatedSessionRequest({
2583
+ requestId: options?.requestId,
2584
+ message: {
2585
+ type: "provider.usage.list.request",
2586
+ },
2587
+ timeout: 30000,
2588
+ });
2589
+ }
2475
2590
  async listCommands(agentId, requestIdOrOptions) {
2476
2591
  const requestId = typeof requestIdOrOptions === "string" ? requestIdOrOptions : requestIdOrOptions?.requestId;
2477
2592
  const draftConfig = typeof requestIdOrOptions === "string" ? undefined : requestIdOrOptions?.draftConfig;
@@ -3134,6 +3249,7 @@ export class DaemonClient {
3134
3249
  }
3135
3250
  }
3136
3251
  disposeTransport(code = 1001, reason = "Reconnecting") {
3252
+ this.stopLivenessHeartbeat();
3137
3253
  this.cleanupTransport();
3138
3254
  if (this.transport) {
3139
3255
  try {
@@ -3217,7 +3333,7 @@ export class DaemonClient {
3217
3333
  }
3218
3334
  this.consecutiveLivenessFailures = 0;
3219
3335
  if (parsed.data.type === "pong") {
3220
- this.resolveLivenessProbe();
3336
+ this.resolvePingProbe();
3221
3337
  this.runtimeMetrics?.recordMessage("pong", bytes, perfNow() - startMs);
3222
3338
  return;
3223
3339
  }
@@ -3231,6 +3347,7 @@ export class DaemonClient {
3231
3347
  tryHandleBinaryFrame(rawBytes) {
3232
3348
  const fileFrame = decodeFileTransferFrame(rawBytes);
3233
3349
  if (fileFrame) {
3350
+ this.consecutiveLivenessFailures = 0;
3234
3351
  this.handleFileTransferFrame(fileFrame);
3235
3352
  this.runtimeMetrics?.recordBinaryFrame("other", rawBytes.byteLength, 0);
3236
3353
  return true;
@@ -3239,6 +3356,7 @@ export class DaemonClient {
3239
3356
  if (!frame) {
3240
3357
  return false;
3241
3358
  }
3359
+ this.consecutiveLivenessFailures = 0;
3242
3360
  const binaryStartMs = perfNow();
3243
3361
  this.terminalStreams.handleFrame(frame);
3244
3362
  let frameKind = "other";
@@ -3344,7 +3462,7 @@ export class DaemonClient {
3344
3462
  // and responses from the previous connection will never arrive.
3345
3463
  this.clearWaiters(new Error(reason ?? "Connection lost"));
3346
3464
  this.rejectPendingSendQueue(new Error(reason ?? "Connection lost"));
3347
- this.rejectLivenessProbe(new Error(reason ?? "Connection lost"));
3465
+ this.rejectPingProbe(new Error(reason ?? "Connection lost"));
3348
3466
  this.terminalStreams.clearSlots();
3349
3467
  this.lastServerInfoMessage = null;
3350
3468
  if (wasDisposed) {
@@ -3382,29 +3500,29 @@ export class DaemonClient {
3382
3500
  this.attemptConnect();
3383
3501
  }, delay);
3384
3502
  }
3385
- resolveLivenessProbe() {
3386
- const probe = this.livenessProbe;
3503
+ resolvePingProbe() {
3504
+ const probe = this.pingProbe;
3387
3505
  if (!probe) {
3388
3506
  return;
3389
3507
  }
3390
- this.livenessProbe = null;
3508
+ this.pingProbe = null;
3391
3509
  clearTimeout(probe.timeoutHandle);
3392
- probe.resolve({ rttMs: perfNow() - probe.startedAt });
3510
+ probe.resolve(perfNow() - probe.startedAt);
3393
3511
  }
3394
- clearLivenessProbe() {
3395
- const probe = this.livenessProbe;
3512
+ clearPingProbe() {
3513
+ const probe = this.pingProbe;
3396
3514
  if (!probe) {
3397
3515
  return;
3398
3516
  }
3399
- this.livenessProbe = null;
3517
+ this.pingProbe = null;
3400
3518
  clearTimeout(probe.timeoutHandle);
3401
3519
  }
3402
- rejectLivenessProbe(error) {
3403
- const probe = this.livenessProbe;
3520
+ rejectPingProbe(error) {
3521
+ const probe = this.pingProbe;
3404
3522
  if (!probe) {
3405
3523
  return;
3406
3524
  }
3407
- this.livenessProbe = null;
3525
+ this.pingProbe = null;
3408
3526
  clearTimeout(probe.timeoutHandle);
3409
3527
  probe.reject(error);
3410
3528
  }
@@ -3431,6 +3549,7 @@ export class DaemonClient {
3431
3549
  this.resetConnectTimeout();
3432
3550
  this.reconnectAttempt = 0;
3433
3551
  this.updateConnectionState({ status: "connected" }, { event: "HELLO_SERVER_INFO" });
3552
+ this.startLivenessHeartbeat();
3434
3553
  this.resubscribeCheckoutDiffSubscriptions();
3435
3554
  this.resubscribeTerminalDirectorySubscriptions();
3436
3555
  this.flushPendingSendQueue();
package/dist/index.d.ts CHANGED
@@ -182,6 +182,7 @@ export interface PaseoAgentHandle {
182
182
  archive(): Promise<{
183
183
  archivedAt: string;
184
184
  }>;
185
+ detach(): Promise<void>;
185
186
  subscribe(handler: (update: PaseoAgentUpdate) => void): () => void;
186
187
  }
187
188
  export interface PaseoAgentActions {
package/dist/index.js CHANGED
@@ -129,6 +129,9 @@ function createAgentHandleFactory(daemonClient) {
129
129
  }
130
130
  return result;
131
131
  },
132
+ detach: async () => {
133
+ await daemonClient.detachAgent(id);
134
+ },
132
135
  subscribe: (handler) => daemonClient.on("agent_update", (message) => {
133
136
  const update = message.payload;
134
137
  if (update.kind === "upsert" && update.agent.id === id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpaseo/client",
3
- "version": "0.1.97-beta.3",
3
+ "version": "0.1.98",
4
4
  "description": "Paseo client SDK package",
5
5
  "files": [
6
6
  "dist",
@@ -35,9 +35,9 @@
35
35
  "test": "vitest run"
36
36
  },
37
37
  "dependencies": {
38
- "@getpaseo/protocol": "0.1.97-beta.3",
39
- "@getpaseo/relay": "0.1.97-beta.3",
40
- "zod": "^3.23.8"
38
+ "@getpaseo/protocol": "0.1.98",
39
+ "@getpaseo/relay": "0.1.98",
40
+ "zod": "^4.4.3"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^20.9.0",