@camerontaylor/paseo-client 0.8.0-fork.3 → 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.
@@ -30,6 +30,7 @@ export declare const DEFAULT_CLIENT_CAPABILITIES: {
30
30
  terminal_reflowable_snapshot: true;
31
31
  provider_subagents: true;
32
32
  side_conversations: true;
33
+ projected_subagent_timeline: true;
33
34
  project_updates: true;
34
35
  compact_provider_snapshots: true;
35
36
  provider_snapshot_references: true;
@@ -103,6 +103,7 @@ export const DEFAULT_CLIENT_CAPABILITIES = {
103
103
  // Nothing conflicts here: the file is upstream-new (#4470), the fork never
104
104
  // edits it, and git never asks. Carried as a branch, not a resolution.
105
105
  [CLIENT_CAPS.sideConversations]: true,
106
+ [CLIENT_CAPS.projectedSubagentTimeline]: true,
106
107
  [CLIENT_CAPS.projectUpdates]: true,
107
108
  [CLIENT_CAPS.compactProviderSnapshots]: true,
108
109
  [CLIENT_CAPS.providerSnapshotReferences]: true,
@@ -0,0 +1,40 @@
1
+ import type { AgentSnapshotPayload, CreationSnapshot, WorkspaceCreateResponse } from "@camerontaylor/paseo-protocol/messages";
2
+ import type { CreateAgentRequestOptions, CreateWorkspaceRequestOptions } from "../daemon-client.js";
3
+ type Kind = CreationSnapshot["kind"];
4
+ export interface CreationResult {
5
+ agent?: AgentSnapshotPayload | null;
6
+ workspace?: WorkspaceCreateResponse["payload"]["workspace"];
7
+ creation?: CreationSnapshot;
8
+ error: string | null;
9
+ errorCode?: string;
10
+ setupTerminalId?: string | null;
11
+ setupSkippedReason?: string;
12
+ requestId?: string;
13
+ }
14
+ interface Dependencies {
15
+ supports: (feature: "creationLifecycle" | "agentRequestReceipts") => boolean;
16
+ requestId: () => string;
17
+ request: (kind: Kind, input: Record<string, unknown>) => Promise<CreationResult>;
18
+ observe: (kind: Kind, key: string, next: (snapshot: CreationSnapshot | null) => void, error: (error: unknown) => void) => () => void;
19
+ legacyAgent: (input: CreateAgentRequestOptions) => Promise<AgentSnapshotPayload>;
20
+ legacyWorkspace: (input: CreateWorkspaceRequestOptions) => Promise<WorkspaceCreateResponse["payload"]>;
21
+ }
22
+ /** Client-owned observation and compatibility. UI callbacks never advance creation. */
23
+ export declare class CreationClient {
24
+ private readonly deps;
25
+ private readonly operations;
26
+ constructor(deps: Dependencies);
27
+ createAgent(input: CreateAgentRequestOptions): Promise<CreationResult>;
28
+ createWorkspace(input: CreateWorkspaceRequestOptions): Promise<CreationResult>;
29
+ private legacyAgent;
30
+ private start;
31
+ private submit;
32
+ receive(snapshot: CreationSnapshot): void;
33
+ reconnect(): void;
34
+ close(): void;
35
+ private finish;
36
+ private fail;
37
+ private notify;
38
+ }
39
+ export {};
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,197 @@
1
+ /** Client-owned observation and compatibility. UI callbacks never advance creation. */
2
+ export class CreationClient {
3
+ constructor(deps) {
4
+ this.deps = deps;
5
+ this.operations = new Map();
6
+ }
7
+ createAgent(input) {
8
+ const { onEvent, ...request } = input;
9
+ return this.start("agent", request, onEvent, () => this.legacyAgent(request));
10
+ }
11
+ createWorkspace(input) {
12
+ const { onEvent, ...request } = input;
13
+ return this.start("workspace", request, onEvent, async (operation) => {
14
+ if (request.workspaceId || request.agent?.agentId)
15
+ throw new Error("Update the host to use caller-selected creation IDs.");
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;
19
+ const workspace = await this.deps.legacyWorkspace(workspaceInput);
20
+ if (workspace.error || !workspace.workspace)
21
+ return workspace;
22
+ this.receive({
23
+ kind: "workspace",
24
+ idempotencyKey: operation.key,
25
+ revision: 1,
26
+ phase: "workspace_ready",
27
+ workspaceId: workspace.workspace.id,
28
+ agentId: null,
29
+ workspace: workspace.workspace,
30
+ setupSkippedReason: workspace.setupSkippedReason,
31
+ error: null,
32
+ });
33
+ if (!agent)
34
+ return workspace;
35
+ const cwd = `${workspace.workspace.workspaceDirectory.replace(/[\\/]+$/, "")}${relativeCwd ?? relativeDirectory(agent.config.cwd, workspace.workspace.projectRootPath)}`;
36
+ const created = await this.legacyAgent({
37
+ ...agent,
38
+ config: { ...agent.config, cwd },
39
+ workspaceId: workspace.workspace.id,
40
+ idempotencyKey: `${operation.key}:agent`,
41
+ });
42
+ return { ...workspace, agent: created.agent };
43
+ });
44
+ }
45
+ async legacyAgent(input) {
46
+ if (input.agentId)
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);
50
+ // COMPAT(creationLifecycle): added in v0.8.0, remove after 2027-03-11 once the daemon floor supports creationLifecycle.
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 };
55
+ }
56
+ start(kind, input, observer, legacy) {
57
+ const key = typeof input.idempotencyKey === "string" ? input.idempotencyKey : this.deps.requestId();
58
+ const identity = JSON.stringify([kind, key]);
59
+ const { requestId: _requestId, ...intent } = input;
60
+ const fingerprint = JSON.stringify(intent, (_key, value) => value && typeof value === "object" && !Array.isArray(value)
61
+ ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)))
62
+ : value);
63
+ const previous = this.operations.get(identity);
64
+ if (previous) {
65
+ if (previous.fingerprint !== fingerprint)
66
+ return Promise.reject(new Error(`${kind}_request_key_conflict`));
67
+ if (observer) {
68
+ previous.observers.add(observer);
69
+ if (previous.snapshot)
70
+ this.notify(observer, previous.snapshot);
71
+ }
72
+ return previous.result;
73
+ }
74
+ let resolve;
75
+ let reject;
76
+ const result = new Promise((done, fail) => {
77
+ resolve = done;
78
+ reject = fail;
79
+ });
80
+ const operation = {
81
+ kind,
82
+ key,
83
+ input: { ...input, idempotencyKey: key },
84
+ fingerprint,
85
+ observers: new Set(observer ? [observer] : []),
86
+ result,
87
+ resolve,
88
+ reject,
89
+ settled: false,
90
+ modern: this.deps.supports("creationLifecycle"),
91
+ recovering: false,
92
+ };
93
+ this.operations.set(identity, operation);
94
+ if (operation.modern)
95
+ void this.submit(operation);
96
+ else
97
+ void legacy(operation).then((value) => this.finish(operation, value), (error) => this.fail(operation, error));
98
+ return result;
99
+ }
100
+ async submit(operation) {
101
+ try {
102
+ const result = await this.deps.request(operation.kind, {
103
+ ...operation.input,
104
+ subscribe: true,
105
+ });
106
+ if (result.creation)
107
+ this.receive(result.creation);
108
+ this.finish(operation, result);
109
+ }
110
+ catch (error) {
111
+ if (operation.settled ||
112
+ (error instanceof Error && "code" in error && error.code === "DAEMON_CONNECTION_LOST"))
113
+ return;
114
+ this.fail(operation, error);
115
+ }
116
+ }
117
+ receive(snapshot) {
118
+ const operation = this.operations.get(JSON.stringify([snapshot.kind, snapshot.idempotencyKey]));
119
+ if (!operation || (operation.snapshot && operation.snapshot.revision > snapshot.revision))
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);
125
+ }
126
+ if (operation.recovering && (snapshot.phase === "completed" || snapshot.phase === "failed")) {
127
+ this.finish(operation, {
128
+ agent: snapshot.agent,
129
+ workspace: snapshot.workspace,
130
+ creation: snapshot,
131
+ setupSkippedReason: snapshot.setupSkippedReason,
132
+ error: snapshot.error,
133
+ errorCode: snapshot.errorCode,
134
+ });
135
+ }
136
+ }
137
+ reconnect() {
138
+ for (const operation of this.operations.values()) {
139
+ if (operation.settled || !operation.modern || operation.recovering)
140
+ continue;
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));
150
+ }
151
+ }
152
+ close() {
153
+ for (const operation of this.operations.values())
154
+ if (!operation.settled)
155
+ this.fail(operation, new Error("Daemon client closed"));
156
+ this.operations.clear();
157
+ }
158
+ finish(operation, result) {
159
+ if (operation.settled)
160
+ return;
161
+ operation.settled = true;
162
+ operation.stopObserving?.();
163
+ operation.resolve(result);
164
+ operation.observers.clear();
165
+ this.operations.delete(JSON.stringify([operation.kind, operation.key]));
166
+ }
167
+ fail(operation, error) {
168
+ if (operation.settled)
169
+ return;
170
+ operation.settled = true;
171
+ operation.stopObserving?.();
172
+ operation.reject(error instanceof Error ? error : new Error(String(error)));
173
+ operation.observers.clear();
174
+ this.operations.delete(JSON.stringify([operation.kind, operation.key]));
175
+ }
176
+ notify(observer, snapshot) {
177
+ try {
178
+ observer(snapshot);
179
+ }
180
+ catch {
181
+ /* Observation does not control execution. */
182
+ }
183
+ }
184
+ }
185
+ function relativeDirectory(cwd, source) {
186
+ const normalize = (value) => value.replace(/\\/g, "/").replace(/\/+$/, "");
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");
191
+ if (normalized === root)
192
+ return "";
193
+ if (!normalized.startsWith(`${root}/`))
194
+ throw new Error("Agent directory must be inside the workspace source");
195
+ return normalized.slice(root.length);
196
+ }
197
+ //# sourceMappingURL=index.js.map
@@ -1,11 +1,12 @@
1
1
  import { type TimelineMessage } from "./timeline-subscription/index.js";
2
2
  import { type OwnedSubscription, type TimelineSubscription } from "./connection/index.js";
3
+ import type { CreationSnapshot } from "@camerontaylor/paseo-protocol/messages";
3
4
  import type { z } from "zod";
4
5
  import type { SessionEventSubscription } from "@camerontaylor/paseo-protocol/messages";
5
6
  import type { ClientCapability } from "@camerontaylor/paseo-protocol/client-capabilities";
6
7
  import type { AgentAttentionNotificationPayload } from "@camerontaylor/paseo-protocol/agent-attention-notification";
7
8
  import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ActiveTurnBehavior, type ServerInfoStatusPayload } from "@camerontaylor/paseo-protocol/messages";
8
- 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";
9
10
  import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@camerontaylor/paseo-protocol/agent-types";
10
11
  import type { AgentConfigApply, MutableDaemonConfig, MutableDaemonConfigPatch } from "@camerontaylor/paseo-protocol/messages";
11
12
  import { type DaemonTransportFactory, type WebSocketFactory } from "./daemon-client-transport.js";
@@ -156,6 +157,8 @@ export interface AgentAttentionRequiredNotification {
156
157
  }
157
158
  type AgentConfigOverrides = Partial<Omit<AgentSessionConfig, "provider" | "cwd">>;
158
159
  export interface CreateAgentRequestOptions extends AgentConfigOverrides {
160
+ agentId?: string;
161
+ onEvent?: (snapshot: CreationSnapshot) => void;
159
162
  config?: AgentSessionConfig;
160
163
  provider?: AgentProvider;
161
164
  cwd?: string;
@@ -175,6 +178,16 @@ export interface CreateAgentRequestOptions extends AgentConfigOverrides {
175
178
  requestId?: string;
176
179
  labels?: Record<string, string>;
177
180
  }
181
+ export interface CreateWorkspaceRequestOptions {
182
+ source: WorkspaceCreateRequest["source"];
183
+ title?: string;
184
+ idempotencyKey?: string;
185
+ workspaceId?: string;
186
+ agent?: Omit<CreateAgentRequestOptions, "workspaceId" | "onEvent" | "worktree" | "git" | "worktreeName" | "idempotencyKey" | "requestId">;
187
+ onEvent?: (snapshot: CreationSnapshot) => void;
188
+ firstAgentContext?: WorkspaceCreateRequest["firstAgentContext"];
189
+ requestId?: string;
190
+ }
178
191
  export interface CreatePaseoWorktreeInput extends Pick<CreatePaseoWorktreeRequest, "cwd" | "projectId" | "worktreeSlug" | "firstAgentContext" | "refName" | "action" | "checkoutSource" | "githubPrNumber"> {
179
192
  }
180
193
  type CheckoutStatusPayload = CheckoutStatusResponse["payload"];
@@ -325,6 +338,14 @@ export interface FetchAgentTimelineOptions {
325
338
  requestId?: string;
326
339
  timeout?: number;
327
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"];
328
349
  export type AgentTimelinePromptIndexPayload = Extract<SessionOutboundMessage, {
329
350
  type: "agent.timeline.list_prompts.response";
330
351
  }>["payload"];
@@ -588,6 +609,7 @@ export declare class DaemonClient {
588
609
  private runtimeMetricsInterval;
589
610
  private runtimeMetrics;
590
611
  private pingProbe;
612
+ private connectionVerification;
591
613
  private livenessHeartbeatTimer;
592
614
  private lastLivenessRttMs;
593
615
  private consecutiveLivenessFailures;
@@ -597,7 +619,10 @@ export declare class DaemonClient {
597
619
  private resolveConnect;
598
620
  private rejectConnect;
599
621
  close(): Promise<void>;
600
- ensureConnected(): void;
622
+ ensureConnected(options?: {
623
+ verify?: boolean;
624
+ }): void;
625
+ private verifyConnection;
601
626
  getConnectionState(): ConnectionState;
602
627
  subscribeConnectionStatus(listener: (status: ConnectionState) => void): () => void;
603
628
  get isConnected(): boolean;
@@ -770,8 +795,9 @@ export declare class DaemonClient {
770
795
  fetchAgent(options: FetchAgentOptions): Promise<FetchAgentResult | null>;
771
796
  fetchAgent(agentId: string, requestId?: string): Promise<FetchAgentResult | null>;
772
797
  fetchAgent(agentId: string, options?: LegacyFetchAgentOptions): Promise<FetchAgentResult | null>;
798
+ private readonly creations;
773
799
  createAgent(options: CreateAgentRequestOptions): Promise<AgentSnapshotPayload>;
774
- private requireAgentRequestReceipts;
800
+ private createLegacyAgent;
775
801
  deleteAgent(agentId: string): Promise<void>;
776
802
  archiveAgent(agentId: string): Promise<{
777
803
  archivedAt: string;
@@ -804,6 +830,7 @@ export declare class DaemonClient {
804
830
  seq: number;
805
831
  epoch: string;
806
832
  }>;
833
+ searchAgentTimeline({ agentId, query, cursor, }: AgentTimelineSearchOptions): Promise<AgentTimelineSearchPayload>;
807
834
  listAgentTimelinePrompts(agentId: string, options?: {
808
835
  requestId?: string;
809
836
  timeout?: number;
@@ -965,11 +992,8 @@ export declare class DaemonClient {
965
992
  scope?: "workspace" | "worktree";
966
993
  }, requestId?: string): Promise<PaseoWorktreeArchivePayload>;
967
994
  createPaseoWorktree(input: CreatePaseoWorktreeInput, requestId?: string): Promise<CreatePaseoWorktreePayload>;
968
- createWorkspace(input: {
969
- source: WorkspaceCreateRequest["source"];
970
- title?: string;
971
- firstAgentContext?: WorkspaceCreateRequest["firstAgentContext"];
972
- }, requestId?: string): Promise<WorkspaceCreatePayload>;
995
+ createWorkspace(input: CreateWorkspaceRequestOptions, requestId?: string): Promise<WorkspaceCreatePayload>;
996
+ private createLegacyWorkspace;
973
997
  validateBranch(options: {
974
998
  cwd: string;
975
999
  branchName: string;
@@ -1171,6 +1195,12 @@ export declare class DaemonClient {
1171
1195
  ref?: string;
1172
1196
  }): Promise<PluginListItem>;
1173
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[]>;
1174
1204
  updatePluginSources(pluginId?: string): Promise<PluginSourceUpdateItem[]>;
1175
1205
  inspectDirectoryPlugin(path: string): Promise<{
1176
1206
  id: string;
@@ -1,8 +1,8 @@
1
1
  import { subscribeTimeline } from "./timeline-subscription/index.js";
2
2
  import { ProviderSnapshotUpdates } from "./provider-snapshots/index.js";
3
3
  import { ConnectionSubscriptions, DEFAULT_CLIENT_CAPABILITIES, } from "./connection/index.js";
4
+ import { CreationClient } from "./creation/index.js";
4
5
  import { CLIENT_CAPS } from "@camerontaylor/paseo-protocol/client-capabilities";
5
- import { parsePluginSourceReference } from "@camerontaylor/paseo-protocol/plugin-source-reference";
6
6
  import { AgentCreateFailedStatusPayloadSchema, AgentCreatedStatusPayloadSchema, AgentRefreshedStatusPayloadSchema, AgentResumedStatusPayloadSchema, parseServerInfoStatusPayload, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, SessionInboundMessageSchema, } from "@camerontaylor/paseo-protocol/messages";
7
7
  import { validateWSOutboundMessage } from "@camerontaylor/paseo-protocol/validation/ws-outbound";
8
8
  import { isRelayClientWebSocketUrl } from "@camerontaylor/paseo-protocol/daemon-endpoints";
@@ -289,9 +289,50 @@ export class DaemonClient {
289
289
  this.runtimeMetricsInterval = null;
290
290
  this.runtimeMetrics = null;
291
291
  this.pingProbe = null;
292
+ this.connectionVerification = null;
292
293
  this.livenessHeartbeatTimer = null;
293
294
  this.lastLivenessRttMs = null;
294
295
  this.consecutiveLivenessFailures = 0;
296
+ // ============================================================================
297
+ // Agent Lifecycle
298
+ // ============================================================================
299
+ this.creations = new CreationClient({
300
+ supports: (feature) => this.lastServerInfoMessage?.features?.[feature] === true,
301
+ requestId: () => this.createRequestId(),
302
+ request: (kind, input) => kind === "workspace"
303
+ ? this.sendCorrelatedSessionRequest({
304
+ requestId: input.requestId,
305
+ message: { ...input, type: "workspace.create.request" },
306
+ responseType: "workspace.create.response",
307
+ timeout: 0,
308
+ })
309
+ : this.sendCorrelatedSessionRequest({
310
+ requestId: input.requestId,
311
+ message: { ...input, type: "agent.create.request" },
312
+ responseType: "agent.create.response",
313
+ timeout: 0,
314
+ }),
315
+ observe: (kind, idempotencyKey, next, error) => {
316
+ const observation = this.observe("creation.subscribe.response", {
317
+ type: "creation.subscribe.request",
318
+ kind,
319
+ idempotencyKey,
320
+ });
321
+ observation.subscribe({
322
+ snapshot: (result) => next(result.snapshot),
323
+ update: (message) => {
324
+ if (message.type === "workspace.create.update" || message.type === "agent.create.update")
325
+ next(message.payload);
326
+ },
327
+ error,
328
+ });
329
+ return () => {
330
+ void observation.release().catch(error);
331
+ };
332
+ },
333
+ legacyAgent: (input) => this.createLegacyAgent(input),
334
+ legacyWorkspace: (input) => this.createLegacyWorkspace(input, input.requestId),
335
+ });
295
336
  this.logger = config.logger ?? consoleLogger;
296
337
  this.logConnectionPath = isRelayClientWebSocketUrl(this.config.url) ? "relay" : "direct";
297
338
  let parsedUrlForLog = null;
@@ -364,6 +405,12 @@ export class DaemonClient {
364
405
  if (this.connectionState.status === "connecting") {
365
406
  return;
366
407
  }
408
+ // This attempt supersedes any retry the last disconnect scheduled. Left
409
+ // armed, that retry would tear down the connection this attempt opens.
410
+ if (this.reconnectTimeout) {
411
+ clearTimeout(this.reconnectTimeout);
412
+ this.reconnectTimeout = null;
413
+ }
367
414
  const headers = {};
368
415
  const password = normalizePassword(this.config.password);
369
416
  if (password) {
@@ -530,6 +577,7 @@ export class DaemonClient {
530
577
  this.providerSnapshotUpdates.clear();
531
578
  this.clearWaiters(new Error("Daemon client closed"));
532
579
  await this.owned.close();
580
+ this.creations.close();
533
581
  this.rejectPendingSendQueue(new Error("Daemon client closed"));
534
582
  this.rejectPingProbe(new Error("Daemon client closed"));
535
583
  this.terminalStreams.clearSlots();
@@ -542,27 +590,50 @@ export class DaemonClient {
542
590
  }
543
591
  this.updateConnectionState({ status: "disposed" }, { event: "DISPOSE", reason: "Client closed", reasonCode: "disposed" });
544
592
  }
545
- ensureConnected() {
593
+ ensureConnected(options) {
546
594
  if (this.connectionState.status === "disposed") {
547
595
  return;
548
596
  }
549
597
  if (!this.shouldReconnect) {
550
598
  this.shouldReconnect = true;
551
599
  }
552
- if (this.connectionState.status === "connected" ||
553
- this.connectionState.status === "connecting") {
600
+ if (this.connectionState.status === "connected") {
601
+ if (options?.verify)
602
+ this.verifyConnection();
554
603
  return;
555
604
  }
556
- if (this.reconnectTimeout) {
557
- clearTimeout(this.reconnectTimeout);
558
- this.reconnectTimeout = null;
559
- }
605
+ if (this.connectionState.status === "connecting")
606
+ return;
560
607
  if (this.connectPromise) {
561
608
  this.attemptConnect();
562
609
  return;
563
610
  }
564
611
  void this.connect();
565
612
  }
613
+ verifyConnection() {
614
+ const transport = this.transport;
615
+ if (!transport || this.connectionVerification === transport)
616
+ return;
617
+ this.connectionVerification = transport;
618
+ // A session probe has its own deadline, independent of a heartbeat that the OS
619
+ // may have suspended. A successful response also proves the session can serve RPCs.
620
+ void this.ping({ timeoutMs: 3000 })
621
+ .catch((error) => {
622
+ if (this.transport !== transport || this.connectionState.status !== "connected")
623
+ return;
624
+ this.disposeTransport(1001, "Connection verification failed");
625
+ this.scheduleReconnect({
626
+ reason: error instanceof Error ? error.message : String(error),
627
+ event: "CONNECTION_VERIFICATION_FAILED",
628
+ reasonCode: "liveness_timeout",
629
+ });
630
+ this.ensureConnected();
631
+ })
632
+ .finally(() => {
633
+ if (this.connectionVerification === transport)
634
+ this.connectionVerification = null;
635
+ });
636
+ }
566
637
  getConnectionState() {
567
638
  return this.connectionState;
568
639
  }
@@ -1479,12 +1550,16 @@ export class DaemonClient {
1479
1550
  }
1480
1551
  return { agent: payload.agent, project: payload.project ?? null };
1481
1552
  }
1482
- // ============================================================================
1483
- // Agent Lifecycle
1484
- // ============================================================================
1485
1553
  async createAgent(options) {
1486
- if (options.idempotencyKey !== undefined)
1487
- this.requireAgentRequestReceipts();
1554
+ const result = await this.creations.createAgent({
1555
+ ...options,
1556
+ config: resolveAgentConfig(options),
1557
+ });
1558
+ if (result.error || !result.agent)
1559
+ throw new Error(result.error ?? "Agent creation failed");
1560
+ return result.agent;
1561
+ }
1562
+ async createLegacyAgent(options) {
1488
1563
  const requestId = this.createRequestId(options.requestId);
1489
1564
  const config = resolveAgentConfig(options);
1490
1565
  const message = SessionInboundMessageSchema.parse({
@@ -1534,12 +1609,6 @@ export class DaemonClient {
1534
1609
  }
1535
1610
  return status.agent;
1536
1611
  }
1537
- requireAgentRequestReceipts() {
1538
- // COMPAT(agentRequestReceipts): added in v0.7.3; remove gate after 2027-03-05.
1539
- if (this.lastServerInfoMessage?.features?.agentRequestReceipts !== true) {
1540
- throw new Error("Update the host to use retry-safe agent creation.");
1541
- }
1542
- }
1543
1612
  async deleteAgent(agentId) {
1544
1613
  const requestId = this.createRequestId();
1545
1614
  const message = SessionInboundMessageSchema.parse({
@@ -1839,6 +1908,17 @@ export class DaemonClient {
1839
1908
  });
1840
1909
  return { seq: payload.seq, epoch: payload.epoch };
1841
1910
  }
1911
+ async searchAgentTimeline({ agentId, query, cursor, }) {
1912
+ const requestId = this.createRequestId();
1913
+ const payload = await this.sendCorrelatedSessionRequest({
1914
+ requestId,
1915
+ message: { type: "agent.timeline.search.request", requestId, agentId, query, cursor },
1916
+ responseType: "agent.timeline.search.response",
1917
+ });
1918
+ if (payload.error)
1919
+ throw new Error(payload.error);
1920
+ return payload;
1921
+ }
1842
1922
  async listAgentTimelinePrompts(agentId, options = {}) {
1843
1923
  const requestId = this.createRequestId(options.requestId);
1844
1924
  const message = SessionInboundMessageSchema.parse({
@@ -1884,6 +1964,10 @@ export class DaemonClient {
1884
1964
  return payload;
1885
1965
  }
1886
1966
  async fetchProviderSubagentTimeline(parentAgentId, subagentId, options = {}) {
1967
+ // COMPAT(projectedSubagentTimeline): added after v0.8.0, remove after 2027-03-14.
1968
+ if (this.lastServerInfoMessage?.features?.projectedSubagentTimeline !== true) {
1969
+ throw new Error("Update the host to view subagent conversations.");
1970
+ }
1887
1971
  const requestId = this.createRequestId(options.requestId);
1888
1972
  const message = SessionInboundMessageSchema.parse({
1889
1973
  type: "agent.provider_subagents.timeline.get.request",
@@ -2892,11 +2976,33 @@ export class DaemonClient {
2892
2976
  });
2893
2977
  }
2894
2978
  async createWorkspace(input, requestId) {
2979
+ const resolvedRequestId = this.createRequestId(requestId ?? input.requestId);
2980
+ const result = await this.creations.createWorkspace({
2981
+ ...input,
2982
+ requestId: resolvedRequestId,
2983
+ ...(input.agent
2984
+ ? { agent: { ...input.agent, config: resolveAgentConfig(input.agent) } }
2985
+ : {}),
2986
+ });
2987
+ return {
2988
+ ...result,
2989
+ workspace: result.workspace ?? null,
2990
+ agent: result.agent ?? undefined,
2991
+ setupTerminalId: result.setupTerminalId ?? null,
2992
+ requestId: result.requestId ?? resolvedRequestId,
2993
+ };
2994
+ }
2995
+ async createLegacyWorkspace(input, requestId) {
2895
2996
  return this.sendCorrelatedSessionRequest({
2896
2997
  requestId,
2897
2998
  message: {
2898
2999
  type: "workspace.create.request",
2899
3000
  source: input.source,
3001
+ // COMPAT(workspaceRequestReceipts): added in v0.8.0; remove after 2027-03-15 once the daemon floor supports workspace receipts.
3002
+ ...(this.lastServerInfoMessage?.features?.workspaceRequestReceipts &&
3003
+ input.idempotencyKey !== undefined
3004
+ ? { idempotencyKey: input.idempotencyKey }
3005
+ : {}),
2900
3006
  ...(input.title !== undefined ? { title: input.title } : {}),
2901
3007
  ...(input.firstAgentContext !== undefined
2902
3008
  ? { firstAgentContext: input.firstAgentContext }
@@ -3080,6 +3186,7 @@ export class DaemonClient {
3080
3186
  if (!bytes) {
3081
3187
  throw new Error("File bytes are required.");
3082
3188
  }
3189
+ const uploadTransport = this.transport;
3083
3190
  const resolvedRequestId = this.createRequestId(input.requestId);
3084
3191
  const modifiedAt = input.modifiedAt ?? new Date().toISOString();
3085
3192
  const responsePromise = this.sendCorrelatedRequest({
@@ -3095,29 +3202,51 @@ export class DaemonClient {
3095
3202
  responseType: "file.upload.response",
3096
3203
  options: { skipQueue: true },
3097
3204
  });
3098
- this.sendBinaryFrame(encodeFileTransferFrame({
3099
- opcode: FileTransferOpcode.FileBegin,
3100
- requestId: resolvedRequestId,
3101
- metadata: {
3102
- mime: input.mimeType,
3103
- size: bytes.byteLength,
3104
- encoding: "binary",
3105
- modifiedAt,
3106
- fileName: input.fileName,
3107
- },
3108
- }));
3109
- const chunkSize = input.chunkSize ?? 1024 * 1024;
3110
- for (let offset = 0; offset < bytes.byteLength; offset += chunkSize) {
3205
+ let settled = false;
3206
+ void responsePromise.then(() => {
3207
+ settled = true;
3208
+ return undefined;
3209
+ }, () => {
3210
+ settled = true;
3211
+ return undefined;
3212
+ });
3213
+ try {
3214
+ this.sendBinaryFrame(encodeFileTransferFrame({
3215
+ opcode: FileTransferOpcode.FileBegin,
3216
+ requestId: resolvedRequestId,
3217
+ metadata: {
3218
+ mime: input.mimeType,
3219
+ size: bytes.byteLength,
3220
+ encoding: "binary",
3221
+ modifiedAt,
3222
+ fileName: input.fileName,
3223
+ },
3224
+ }));
3225
+ const chunkSize = input.chunkSize ?? 128 * 1024;
3226
+ for (let offset = 0; offset < bytes.byteLength; offset += chunkSize) {
3227
+ // Native WebSocket.send encodes binary synchronously. Let rendering and
3228
+ // incoming messages run between bounded pieces on every platform.
3229
+ await new Promise((resolve) => setTimeout(resolve, 0));
3230
+ if (settled)
3231
+ return await responsePromise;
3232
+ if (this.transport !== uploadTransport || this.connectionState.status !== "connected") {
3233
+ throw new DaemonConnectionError("Connection changed during file upload");
3234
+ }
3235
+ this.sendBinaryFrame(encodeFileTransferFrame({
3236
+ opcode: FileTransferOpcode.FileChunk,
3237
+ requestId: resolvedRequestId,
3238
+ payload: bytes.subarray(offset, Math.min(offset + chunkSize, bytes.byteLength)),
3239
+ }));
3240
+ }
3111
3241
  this.sendBinaryFrame(encodeFileTransferFrame({
3112
- opcode: FileTransferOpcode.FileChunk,
3242
+ opcode: FileTransferOpcode.FileEnd,
3113
3243
  requestId: resolvedRequestId,
3114
- payload: bytes.subarray(offset, Math.min(offset + chunkSize, bytes.byteLength)),
3115
3244
  }));
3116
3245
  }
3117
- this.sendBinaryFrame(encodeFileTransferFrame({
3118
- opcode: FileTransferOpcode.FileEnd,
3119
- requestId: resolvedRequestId,
3120
- }));
3246
+ catch (error) {
3247
+ this.rejectWaitersForRequestId(resolvedRequestId, error instanceof Error ? error : new Error(String(error)));
3248
+ throw error;
3249
+ }
3121
3250
  return responsePromise;
3122
3251
  }
3123
3252
  async requestDownloadToken(cwd, path, requestId) {
@@ -3467,18 +3596,21 @@ export class DaemonClient {
3467
3596
  }
3468
3597
  async installPluginSource(input) {
3469
3598
  const requestId = this.createRequestId();
3470
- const reference = parsePluginSourceReference(input.source);
3599
+ // COMPAT(pluginSourceInstallation): added in v0.8.0; remove after 2027-03-16 once daemon floor supports source identifiers.
3600
+ if (this.getLastServerInfoMessage()?.features?.pluginSourceInstallation !== true) {
3601
+ throw new Error("Update the host to install plugin sources.");
3602
+ }
3471
3603
  const payload = await this.sendCorrelatedSessionRequest({
3472
3604
  requestId,
3473
3605
  message: {
3474
3606
  type: "plugin.source.install.request",
3475
3607
  requestId,
3476
- source: reference.source,
3477
- ...(reference.pluginPath ? { pluginPath: reference.pluginPath } : {}),
3608
+ source: input.source,
3478
3609
  ...(input.id ? { id: input.id } : {}),
3479
3610
  ...(input.ref ? { ref: input.ref } : {}),
3480
3611
  },
3481
3612
  responseType: "plugin.source.install.response",
3613
+ timeout: 5 * 60 * 1000,
3482
3614
  });
3483
3615
  return payload.plugin;
3484
3616
  }
@@ -3495,6 +3627,33 @@ export class DaemonClient {
3495
3627
  });
3496
3628
  return payload.plugins;
3497
3629
  }
3630
+ requirePluginUpdates() {
3631
+ // COMPAT(pluginSourceUpdates): added in v0.8.0; remove after 2027-03-16 once daemon floor supports reviewed updates.
3632
+ if (this.getLastServerInfoMessage()?.features?.pluginSourceUpdates !== true)
3633
+ throw new Error("Update the host to review plugin updates.");
3634
+ }
3635
+ async previewPluginUpdates(input = {}) {
3636
+ this.requirePluginUpdates();
3637
+ const requestId = this.createRequestId();
3638
+ const payload = await this.sendCorrelatedSessionRequest({
3639
+ requestId,
3640
+ message: { type: "plugin.source.update.preview.request", requestId, ...input },
3641
+ responseType: "plugin.source.update.preview.response",
3642
+ timeout: 300000,
3643
+ });
3644
+ return payload.plugins;
3645
+ }
3646
+ async applyPluginUpdates(proposals) {
3647
+ this.requirePluginUpdates();
3648
+ const requestId = this.createRequestId();
3649
+ const payload = await this.sendCorrelatedSessionRequest({
3650
+ requestId,
3651
+ message: { type: "plugin.source.update.apply.request", requestId, proposals },
3652
+ responseType: "plugin.source.update.apply.response",
3653
+ timeout: 300000,
3654
+ });
3655
+ return payload.plugins;
3656
+ }
3498
3657
  async updatePluginSources(pluginId) {
3499
3658
  const requestId = this.createRequestId();
3500
3659
  const payload = await this.sendCorrelatedSessionRequest({
@@ -4437,6 +4596,7 @@ export class DaemonClient {
4437
4596
  ...DEFAULT_CLIENT_CAPABILITIES,
4438
4597
  ...this.config.capabilities,
4439
4598
  });
4599
+ this.creations.reconnect();
4440
4600
  this.flushPendingSendQueue();
4441
4601
  this.resolveConnect();
4442
4602
  }
@@ -4469,6 +4629,11 @@ export class DaemonClient {
4469
4629
  handler(event);
4470
4630
  }
4471
4631
  }
4632
+ if (consumerMessage.type === "workspace.create.update" ||
4633
+ consumerMessage.type === "agent.create.update") {
4634
+ if (!consumerMessage.payload.subscriptionId)
4635
+ this.creations.receive(consumerMessage.payload);
4636
+ }
4472
4637
  this.resolveWaiters(consumerMessage);
4473
4638
  this.owned.receive(consumerMessage);
4474
4639
  }
@@ -4631,7 +4796,7 @@ export class DaemonClient {
4631
4796
  }
4632
4797
  }
4633
4798
  function resolveAgentConfig(options) {
4634
- const { config, provider, cwd, env: _env, workspaceId: _workspaceId, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
4799
+ const { config, provider, cwd, agentId: _agentId, onEvent: _onEvent, idempotencyKey: _idempotencyKey, clientMessageId: _clientMessageId, callerAgentId: _callerAgentId, outputSchema: _outputSchema, attachments: _attachments, worktree: _worktree, autoArchive: _autoArchive, env: _env, workspaceId: _workspaceId, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
4635
4800
  const baseConfig = {
4636
4801
  ...(provider ? { provider } : {}),
4637
4802
  ...(cwd ? { cwd } : {}),
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { OwnedSubscription } from "./connection/index.js";
2
2
  export type { OwnedSubscription, SubscriptionObserver } from "./connection/index.js";
3
3
  import type { DaemonClientConfig } from "./daemon-client.js";
4
4
  import type { AgentPermissionResponse } from "@camerontaylor/paseo-protocol/agent-types";
5
- import type { AgentSnapshotPayload, CreateAgentRequestMessage, FetchWorkspacesRequestMessage, FetchWorkspacesResponseMessage, GetProvidersSnapshotResponseMessage, ListAvailableProvidersResponse, ListCommandsResponse, ListProviderFeaturesRequestMessage, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ProjectListRequestMessage, ProjectListResponseMessage, ListProviderModesResponseMessage, MutableDaemonConfig, MutableDaemonConfigPatch, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, ProjectPlacementPayload, WorkspaceProjectDescriptorPayload, RefreshProvidersSnapshotResponseMessage, SendAgentMessageRequest, SessionOutboundMessage, WorkspaceDescriptorPayload, WorkspaceCreateRequest } from "@camerontaylor/paseo-protocol/messages";
5
+ import type { AgentSnapshotPayload, CreationSnapshot, CreateAgentRequestMessage, FetchWorkspacesRequestMessage, FetchWorkspacesResponseMessage, GetProvidersSnapshotResponseMessage, ListAvailableProvidersResponse, ListCommandsResponse, ListProviderFeaturesRequestMessage, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ProjectListRequestMessage, ProjectListResponseMessage, ListProviderModesResponseMessage, MutableDaemonConfig, MutableDaemonConfigPatch, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, ProjectPlacementPayload, WorkspaceProjectDescriptorPayload, RefreshProvidersSnapshotResponseMessage, SendAgentMessageRequest, SessionOutboundMessage, WorkspaceDescriptorPayload, WorkspaceCreateRequest } from "@camerontaylor/paseo-protocol/messages";
6
6
  import { DaemonClient } from "./daemon-client.js";
7
7
  import { type PaseoTerminalActions, type PaseoWorkspaceTerminalActions } from "./terminals/index.js";
8
8
  export type { PaseoTerminal, PaseoTerminalActions, PaseoTerminalHandle, PaseoTerminalCreateOptions, PaseoTerminalListOptions, PaseoTerminalListResult, PaseoTerminalCaptureOptions, PaseoTerminalCaptureResult, PaseoWorkspaceTerminalActions, } from "./terminals/index.js";
@@ -83,8 +83,10 @@ export interface PaseoWorkspaceOpenOptions {
83
83
  cwd: string;
84
84
  requestId?: string;
85
85
  }
86
- export type PaseoWorkspaceCreateOptions = Omit<WorkspaceCreateRequest, "type" | "requestId"> & {
86
+ export type PaseoWorkspaceCreateOptions = Omit<WorkspaceCreateRequest, "type" | "requestId" | "agent" | "subscribe"> & {
87
87
  requestId?: string;
88
+ agent?: Omit<PaseoAgentCreateOptions, "worktree" | "git" | "onEvent" | "idempotencyKey" | "requestId">;
89
+ onEvent?: (snapshot: CreationSnapshot) => void;
88
90
  };
89
91
  export interface PaseoWorkspaceArchiveResult {
90
92
  requestId: string;
@@ -160,6 +162,9 @@ export interface PaseoAgentConfig {
160
162
  mcpServers?: PaseoAgentSessionConfig["mcpServers"];
161
163
  }
162
164
  export interface PaseoAgentCreateOptions {
165
+ idempotencyKey?: string;
166
+ agentId?: string;
167
+ onEvent?: (snapshot: CreationSnapshot) => void;
163
168
  config: PaseoAgentConfig;
164
169
  cwd: string;
165
170
  parent?: string | PaseoAgentHandle;
package/dist/index.js CHANGED
@@ -27,6 +27,25 @@ export function createPaseoClient(config) {
27
27
  getConnectionState: () => daemonClient.getConnectionState(),
28
28
  };
29
29
  }
30
+ function toDaemonAgentCreateOptions(options, placement) {
31
+ const { config: agentConfig, cwd, parent, title, prompt, ...requestOptions } = options;
32
+ const { provider: providerModel, options: providerOptions, ...runtimeConfig } = agentConfig;
33
+ const { provider, model } = parseProviderModel(providerModel);
34
+ return {
35
+ ...requestOptions,
36
+ config: {
37
+ ...runtimeConfig,
38
+ provider,
39
+ model,
40
+ cwd: placement?.cwd ?? cwd,
41
+ ...(title !== undefined ? { title } : {}),
42
+ ...(providerOptions !== undefined ? { providerOptions } : {}),
43
+ },
44
+ ...(placement ? { workspaceId: placement.workspaceId } : {}),
45
+ ...(parent ? { callerAgentId: resolveAgentId(parent) } : {}),
46
+ ...(prompt !== undefined ? { initialPrompt: prompt } : {}),
47
+ };
48
+ }
30
49
  export function createPaseoApi(daemonClient, scopeOptions) {
31
50
  const handles = new Set();
32
51
  const agentListeners = new Set();
@@ -62,24 +81,7 @@ export function createPaseoApi(daemonClient, scopeOptions) {
62
81
  };
63
82
  const createAgentHandle = createAgentHandleFactory(daemonClient, listenAgents, (agentId, handler) => own(() => daemonClient.subscribeAgentTimeline(agentId, handler)));
64
83
  const createAgent = async (options, placement) => {
65
- const { config: agentConfig, cwd, parent, title, prompt, ...requestOptions } = options;
66
- const { provider: providerModel, options: providerOptions, ...runtimeConfig } = agentConfig;
67
- const { provider, model } = parseProviderModel(providerModel);
68
- const effectiveCwd = placement?.cwd ?? cwd;
69
- const agent = await daemonClient.createAgent({
70
- ...requestOptions,
71
- config: {
72
- ...runtimeConfig,
73
- provider,
74
- model,
75
- cwd: effectiveCwd,
76
- ...(title !== undefined ? { title } : {}),
77
- ...(providerOptions !== undefined ? { providerOptions } : {}),
78
- },
79
- ...(placement ? { workspaceId: placement.workspaceId } : {}),
80
- ...(parent ? { callerAgentId: resolveAgentId(parent) } : {}),
81
- ...(prompt !== undefined ? { initialPrompt: prompt } : {}),
82
- });
84
+ const agent = await daemonClient.createAgent(toDaemonAgentCreateOptions(options, placement));
83
85
  return createAgentHandle(agent);
84
86
  };
85
87
  const terminals = createTerminalActions(daemonClient, async (workspaceId) => {
@@ -173,8 +175,8 @@ export function createPaseoApi(daemonClient, scopeOptions) {
173
175
  list: listWorkspaces,
174
176
  ref: (workspace) => createWorkspaceHandle(workspace),
175
177
  open: (input, requestId) => openWorkspace(daemonClient, createWorkspaceHandle, input, requestId),
176
- create: async ({ requestId, ...options }) => {
177
- const result = await daemonClient.createWorkspace(options, requestId);
178
+ create: async ({ requestId, agent, ...options }) => {
179
+ const result = await daemonClient.createWorkspace({ ...options, ...(agent ? { agent: toDaemonAgentCreateOptions(agent) } : {}) }, requestId);
178
180
  if (result.error || !result.workspace) {
179
181
  throw new Error(result.error ?? "The daemon did not create a workspace");
180
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camerontaylor/paseo-client",
3
- "version": "0.8.0-fork.3",
3
+ "version": "0.9.0-beta.2.fork.1",
4
4
  "description": "Paseo client SDK package",
5
5
  "files": [
6
6
  "dist",
@@ -39,8 +39,8 @@
39
39
  "test": "vitest run"
40
40
  },
41
41
  "dependencies": {
42
- "@camerontaylor/paseo-protocol": "0.8.0-fork.3",
43
- "@camerontaylor/paseo-relay": "0.8.0-fork.3",
42
+ "@camerontaylor/paseo-protocol": "0.9.0-beta.2.fork.1",
43
+ "@camerontaylor/paseo-relay": "0.9.0-beta.2.fork.1",
44
44
  "zod": "^4.4.3"
45
45
  },
46
46
  "devDependencies": {