@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.
- package/dist/connection/index.d.ts +1 -0
- package/dist/connection/index.js +1 -0
- package/dist/creation/index.d.ts +40 -0
- package/dist/creation/index.js +197 -0
- package/dist/daemon-client.d.ts +38 -8
- package/dist/daemon-client.js +207 -42
- package/dist/index.d.ts +7 -2
- package/dist/index.js +22 -20
- package/package.json +3 -3
|
@@ -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;
|
package/dist/connection/index.js
CHANGED
|
@@ -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
|
package/dist/daemon-client.d.ts
CHANGED
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
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;
|
package/dist/daemon-client.js
CHANGED
|
@@ -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
|
-
|
|
600
|
+
if (this.connectionState.status === "connected") {
|
|
601
|
+
if (options?.verify)
|
|
602
|
+
this.verifyConnection();
|
|
554
603
|
return;
|
|
555
604
|
}
|
|
556
|
-
if (this.
|
|
557
|
-
|
|
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
|
-
|
|
1487
|
-
|
|
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
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
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.
|
|
3242
|
+
opcode: FileTransferOpcode.FileEnd,
|
|
3113
3243
|
requestId: resolvedRequestId,
|
|
3114
|
-
payload: bytes.subarray(offset, Math.min(offset + chunkSize, bytes.byteLength)),
|
|
3115
3244
|
}));
|
|
3116
3245
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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.
|
|
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.
|
|
43
|
-
"@camerontaylor/paseo-relay": "0.
|
|
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": {
|