@getpaseo/server 0.1.91 → 0.1.93
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/server/server/agent/agent-manager.d.ts +12 -4
- package/dist/server/server/agent/agent-manager.js +87 -26
- package/dist/server/server/agent/agent-projections.d.ts +4 -2
- package/dist/server/server/agent/agent-projections.js +8 -28
- package/dist/server/server/agent/agent-sdk-types.d.ts +30 -10
- package/dist/server/server/agent/import-sessions.d.ts +3 -2
- package/dist/server/server/agent/import-sessions.js +23 -55
- package/dist/server/server/agent/provider-registry.js +34 -13
- package/dist/server/server/agent/provider-session-import.d.ts +10 -0
- package/dist/server/server/agent/provider-session-import.js +49 -0
- package/dist/server/server/agent/providers/acp-agent.d.ts +12 -2
- package/dist/server/server/agent/providers/acp-agent.js +78 -36
- package/dist/server/server/agent/providers/claude/agent.d.ts +3 -2
- package/dist/server/server/agent/providers/claude/agent.js +28 -24
- package/dist/server/server/agent/providers/claude/models.js +15 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +3 -2
- package/dist/server/server/agent/providers/codex-app-server-agent.js +20 -21
- package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +1 -0
- package/dist/server/server/agent/providers/cursor-acp-agent.js +1 -0
- package/dist/server/server/agent/providers/generic-acp-agent.d.ts +9 -0
- package/dist/server/server/agent/providers/generic-acp-agent.js +18 -1
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +3 -2
- package/dist/server/server/agent/providers/mock-load-test-agent.js +11 -1
- package/dist/server/server/agent/providers/mock-slow-provider.d.ts +1 -2
- package/dist/server/server/agent/providers/mock-slow-provider.js +0 -3
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +8 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +3 -2
- package/dist/server/server/agent/providers/opencode-agent.js +60 -32
- package/dist/server/server/agent/providers/pi/agent.d.ts +3 -2
- package/dist/server/server/agent/providers/pi/agent.js +28 -5
- package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +3 -4
- package/dist/server/server/agent/providers/pi/session-descriptor.js +17 -37
- package/dist/server/server/session.js +0 -1
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AGENT_LIFECYCLE_STATUSES, type AgentLifecycleStatus } from "@getpaseo/protocol/agent-lifecycle";
|
|
2
2
|
import type { Logger } from "pino";
|
|
3
3
|
import type { TerminalManager } from "../../terminal/terminal-manager.js";
|
|
4
|
-
import { type AgentCapabilityFlags, type AgentClient, type AgentFeature, type AgentSlashCommand, type AgentMode, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPermissionResult, type AgentPersistenceHandle, type AgentPromptInput, type AgentProvider, type AgentRunOptions, type AgentRunResult, type AgentSession, type AgentSessionConfig, type AgentStreamEvent, type AgentTimelineItem, type AgentUsage, type AgentRuntimeInfo, type
|
|
4
|
+
import { type AgentCapabilityFlags, type AgentClient, type AgentFeature, type AgentSlashCommand, type AgentMode, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPermissionResult, type AgentPersistenceHandle, type AgentPromptInput, type AgentProvider, type AgentRunOptions, type AgentRunResult, type AgentSession, type AgentSessionConfig, type AgentStreamEvent, type AgentTimelineItem, type AgentUsage, type AgentRuntimeInfo, type ImportableProviderSession, type ListImportableSessionsOptions } from "./agent-sdk-types.js";
|
|
5
5
|
import type { StoredAgentRecord, AgentStorage } from "./agent-storage.js";
|
|
6
6
|
import type { AgentTimelineFetchOptions, AgentTimelineFetchResult, AgentTimelineRow, AgentTimelineStore } from "./agent-timeline-store-types.js";
|
|
7
7
|
import { type ForegroundTurnWaiter } from "./foreground-run-state.js";
|
|
@@ -28,13 +28,16 @@ interface HydrateTimelineOptions {
|
|
|
28
28
|
force?: boolean;
|
|
29
29
|
broadcast?: boolean;
|
|
30
30
|
}
|
|
31
|
-
export type ImportablePersistedAgentQueryOptions =
|
|
31
|
+
export type ImportablePersistedAgentQueryOptions = ListImportableSessionsOptions & {
|
|
32
32
|
/**
|
|
33
33
|
* When set, only providers in this set are scanned, in addition to the
|
|
34
34
|
* built-in importable allowlist + enabled + non-derived rules.
|
|
35
35
|
*/
|
|
36
36
|
providerFilter?: Set<string>;
|
|
37
37
|
};
|
|
38
|
+
export interface ManagedImportableProviderSession extends ImportableProviderSession {
|
|
39
|
+
provider: AgentProvider;
|
|
40
|
+
}
|
|
38
41
|
export type AgentAttentionCallback = (params: {
|
|
39
42
|
agentId: string;
|
|
40
43
|
provider: AgentProvider;
|
|
@@ -198,9 +201,8 @@ export declare class AgentManager {
|
|
|
198
201
|
hasInFlightRun(agentId: string): boolean;
|
|
199
202
|
subscribe(callback: AgentSubscriber, options?: SubscribeOptions): () => void;
|
|
200
203
|
listAgents(): ManagedAgent[];
|
|
201
|
-
|
|
204
|
+
listImportableSessions(options?: ImportablePersistedAgentQueryOptions): Promise<ManagedImportableProviderSession[]>;
|
|
202
205
|
private isProviderImportable;
|
|
203
|
-
findPersistedAgent(provider: AgentProvider, sessionId: string, options?: Pick<ListPersistedAgentsOptions, "cwd">): Promise<PersistedAgentDescriptor | null>;
|
|
204
206
|
listProviderAvailability(): Promise<ProviderAvailability[]>;
|
|
205
207
|
listDraftCommands(config: AgentSessionConfig): Promise<AgentSlashCommand[]>;
|
|
206
208
|
listDraftFeatures(config: AgentSessionConfig): Promise<AgentFeature[]>;
|
|
@@ -223,6 +225,12 @@ export declare class AgentManager {
|
|
|
223
225
|
lastUserMessageAt?: Date | null;
|
|
224
226
|
labels?: Record<string, string>;
|
|
225
227
|
}): Promise<ManagedAgent>;
|
|
228
|
+
importProviderSession(input: {
|
|
229
|
+
provider: AgentProvider;
|
|
230
|
+
providerHandleId: string;
|
|
231
|
+
cwd: string;
|
|
232
|
+
labels?: Record<string, string>;
|
|
233
|
+
}): Promise<ManagedAgent>;
|
|
226
234
|
reloadAgentSession(agentId: string, overrides?: Partial<AgentSessionConfig>, options?: {
|
|
227
235
|
rehydrateFromDisk?: boolean;
|
|
228
236
|
}): Promise<ManagedAgent>;
|
|
@@ -13,6 +13,7 @@ import { getAgentProviderDefinition } from "@getpaseo/protocol/provider-manifest
|
|
|
13
13
|
import { invokeRewindCapability } from "./rewind/rewind.js";
|
|
14
14
|
import { isSystemInjectedEnvelope } from "./agent-prompt.js";
|
|
15
15
|
import { stripInternalPaseoMcpServer, withRuntimePaseoMcpServer } from "./runtime-mcp-config.js";
|
|
16
|
+
import { resolveCreateAgentTitles } from "./create-agent-title.js";
|
|
16
17
|
const RELOAD_SESSION_CLOSE_TIMEOUT_MS = 3000;
|
|
17
18
|
const INTERRUPT_SESSION_TIMEOUT_MS = 2000;
|
|
18
19
|
const STORED_AGENT_CAPABILITIES = {
|
|
@@ -122,6 +123,44 @@ function buildExplicitTimelineSeedForRegister(now, options) {
|
|
|
122
123
|
timestamp: (options?.updatedAt ?? options?.createdAt ?? now).toISOString(),
|
|
123
124
|
};
|
|
124
125
|
}
|
|
126
|
+
function buildImportedTimelineRows(entries) {
|
|
127
|
+
const rows = [];
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
if (entry.item.type === "user_message" && isSystemInjectedEnvelope(entry.item.text)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
rows.push({
|
|
133
|
+
seq: rows.length + 1,
|
|
134
|
+
timestamp: entry.timestamp ?? new Date().toISOString(),
|
|
135
|
+
item: entry.item,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return rows;
|
|
139
|
+
}
|
|
140
|
+
function resolveImportedAgentTitle(config, timelineRows) {
|
|
141
|
+
const initialPrompt = getFirstUserMessageTextFromRows(timelineRows);
|
|
142
|
+
if (!initialPrompt) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const { explicitTitle, provisionalTitle } = resolveCreateAgentTitles({
|
|
146
|
+
configTitle: config.title,
|
|
147
|
+
initialPrompt,
|
|
148
|
+
});
|
|
149
|
+
return explicitTitle ?? provisionalTitle ?? null;
|
|
150
|
+
}
|
|
151
|
+
function getFirstUserMessageTextFromRows(rows) {
|
|
152
|
+
for (const row of rows) {
|
|
153
|
+
const item = row.item;
|
|
154
|
+
if (item.type !== "user_message") {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const text = item.text.trim();
|
|
158
|
+
if (text) {
|
|
159
|
+
return text;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
125
164
|
export class AgentManager {
|
|
126
165
|
constructor(options) {
|
|
127
166
|
this.clients = new Map();
|
|
@@ -279,24 +318,25 @@ export class AgentManager {
|
|
|
279
318
|
.filter((agent) => !agent.internal)
|
|
280
319
|
.map((agent) => Object.assign({}, agent));
|
|
281
320
|
}
|
|
282
|
-
async
|
|
283
|
-
const providerEntries = Array.from(this.clients.entries()).filter(([provider, client]) =>
|
|
321
|
+
async listImportableSessions(options) {
|
|
322
|
+
const providerEntries = Array.from(this.clients.entries()).filter(([provider, client]) => client.capabilities.supportsSessionListing &&
|
|
323
|
+
!!client.listImportableSessions &&
|
|
284
324
|
this.isProviderImportable(provider, options?.providerFilter));
|
|
285
|
-
const
|
|
325
|
+
const sessionLists = await Promise.all(providerEntries.map(async ([provider, client]) => {
|
|
286
326
|
try {
|
|
287
|
-
return await client.
|
|
327
|
+
return (await client.listImportableSessions({
|
|
288
328
|
limit: options?.limit,
|
|
289
329
|
cwd: options?.cwd,
|
|
290
|
-
});
|
|
330
|
+
})).map((session) => Object.assign(session, { provider }));
|
|
291
331
|
}
|
|
292
332
|
catch (error) {
|
|
293
|
-
this.logger.warn({ err: error, provider }, "Failed to list
|
|
333
|
+
this.logger.warn({ err: error, provider }, "Failed to list importable sessions for provider");
|
|
294
334
|
return [];
|
|
295
335
|
}
|
|
296
336
|
}));
|
|
297
|
-
const
|
|
337
|
+
const sessions = sessionLists.flat();
|
|
298
338
|
const limit = options?.limit ?? 20;
|
|
299
|
-
return
|
|
339
|
+
return sessions
|
|
300
340
|
.sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime())
|
|
301
341
|
.slice(0, limit);
|
|
302
342
|
}
|
|
@@ -309,16 +349,6 @@ export class AgentManager {
|
|
|
309
349
|
}
|
|
310
350
|
return true;
|
|
311
351
|
}
|
|
312
|
-
async findPersistedAgent(provider, sessionId, options) {
|
|
313
|
-
const client = this.requireClient(provider);
|
|
314
|
-
if (!client.listPersistedAgents) {
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
const descriptors = await client.listPersistedAgents({ limit: 200, cwd: options?.cwd });
|
|
318
|
-
return (descriptors.find((descriptor) => {
|
|
319
|
-
return (descriptor.sessionId === sessionId || descriptor.persistence.nativeHandle === sessionId);
|
|
320
|
-
}) ?? null);
|
|
321
|
-
}
|
|
322
352
|
async listProviderAvailability() {
|
|
323
353
|
const checks = Array.from(this.clients.keys()).map(async (provider) => {
|
|
324
354
|
const client = this.clients.get(provider);
|
|
@@ -458,6 +488,35 @@ export class AgentManager {
|
|
|
458
488
|
const session = await client.resumeSession(handle, launchConfig, launchContext);
|
|
459
489
|
return this.registerSession(session, storedConfig, resolvedAgentId, options);
|
|
460
490
|
}
|
|
491
|
+
async importProviderSession(input) {
|
|
492
|
+
const resolvedAgentId = validateAgentId(this.idFactory(), "importProviderSession");
|
|
493
|
+
this.requireEnabledProvider(input.provider);
|
|
494
|
+
const client = await this.requireAvailableClient({ provider: input.provider });
|
|
495
|
+
if (!client.importSession) {
|
|
496
|
+
throw new Error(`Provider '${input.provider}' does not support importing sessions`);
|
|
497
|
+
}
|
|
498
|
+
const { storedConfig, launchConfig } = await this.prepareSessionConfig({
|
|
499
|
+
provider: input.provider,
|
|
500
|
+
cwd: input.cwd,
|
|
501
|
+
}, resolvedAgentId);
|
|
502
|
+
const launchContext = this.buildLaunchContext(resolvedAgentId);
|
|
503
|
+
const imported = await client.importSession({
|
|
504
|
+
providerHandleId: input.providerHandleId,
|
|
505
|
+
cwd: input.cwd,
|
|
506
|
+
}, { config: launchConfig, storedConfig, launchContext });
|
|
507
|
+
const importedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(imported.config));
|
|
508
|
+
const timelineRows = buildImportedTimelineRows(imported.timeline);
|
|
509
|
+
const initialTitle = resolveImportedAgentTitle(importedConfig, timelineRows);
|
|
510
|
+
return this.registerSession(imported.session, importedConfig, resolvedAgentId, {
|
|
511
|
+
labels: input.labels,
|
|
512
|
+
timelineRows,
|
|
513
|
+
timelineNextSeq: timelineRows.length + 1,
|
|
514
|
+
persistence: imported.persistence,
|
|
515
|
+
historyPrimed: true,
|
|
516
|
+
initialTitle,
|
|
517
|
+
publishWhenReady: true,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
461
520
|
// Hot-reload an active agent session with config overrides. By default the
|
|
462
521
|
// in-memory timeline is preserved (used for voice-mode toggles and similar
|
|
463
522
|
// config swaps). When `rehydrateFromDisk` is set, the timeline is wiped so a
|
|
@@ -1605,13 +1664,15 @@ export class AgentManager {
|
|
|
1605
1664
|
this.agents.set(resolvedAgentId, managed);
|
|
1606
1665
|
// Initialize previousStatus to track transitions
|
|
1607
1666
|
this.previousStatuses.set(resolvedAgentId, managed.lifecycle);
|
|
1608
|
-
await this.refreshRuntimeInfo(managed);
|
|
1667
|
+
await this.refreshRuntimeInfo(managed, { emit: !options?.publishWhenReady });
|
|
1609
1668
|
await this.persistSnapshot(managed, {
|
|
1610
1669
|
workspaceId: options?.workspaceId,
|
|
1611
1670
|
title: initialPersistedTitle,
|
|
1612
1671
|
});
|
|
1613
|
-
|
|
1614
|
-
|
|
1672
|
+
if (!options?.publishWhenReady) {
|
|
1673
|
+
this.emitState(managed, { persist: false });
|
|
1674
|
+
}
|
|
1675
|
+
await this.refreshSessionState(managed, { emit: !options?.publishWhenReady });
|
|
1615
1676
|
managed.lifecycle = "idle";
|
|
1616
1677
|
await this.persistSnapshot(managed, { workspaceId: options?.workspaceId });
|
|
1617
1678
|
this.emitState(managed, { persist: false });
|
|
@@ -1660,7 +1721,7 @@ export class AgentManager {
|
|
|
1660
1721
|
foregroundTurnWaiters: new Set(),
|
|
1661
1722
|
finalizedForegroundTurnIds: new Set(),
|
|
1662
1723
|
unsubscribeSession: null,
|
|
1663
|
-
persistence: attachPersistenceCwd(session.describePersistence(), config.cwd),
|
|
1724
|
+
persistence: attachPersistenceCwd(options?.persistence ?? session.describePersistence(), config.cwd),
|
|
1664
1725
|
historyPrimed: options?.historyPrimed ?? durableTimelineHasRows,
|
|
1665
1726
|
lastUserMessageAt: options?.lastUserMessageAt ?? null,
|
|
1666
1727
|
lastUsage: options?.lastUsage,
|
|
@@ -1812,7 +1873,7 @@ export class AgentManager {
|
|
|
1812
1873
|
}
|
|
1813
1874
|
return this.registry;
|
|
1814
1875
|
}
|
|
1815
|
-
async refreshSessionState(agent) {
|
|
1876
|
+
async refreshSessionState(agent, options) {
|
|
1816
1877
|
try {
|
|
1817
1878
|
const modes = await agent.session.getAvailableModes();
|
|
1818
1879
|
agent.availableModes = modes;
|
|
@@ -1834,9 +1895,9 @@ export class AgentManager {
|
|
|
1834
1895
|
agent.pendingPermissions.clear();
|
|
1835
1896
|
}
|
|
1836
1897
|
this.syncFeaturesFromSession(agent);
|
|
1837
|
-
await this.refreshRuntimeInfo(agent);
|
|
1898
|
+
await this.refreshRuntimeInfo(agent, options);
|
|
1838
1899
|
}
|
|
1839
|
-
async refreshRuntimeInfo(agent) {
|
|
1900
|
+
async refreshRuntimeInfo(agent, options) {
|
|
1840
1901
|
try {
|
|
1841
1902
|
const newInfo = await agent.session.getRuntimeInfo();
|
|
1842
1903
|
const changed = newInfo.model !== agent.runtimeInfo?.model ||
|
|
@@ -1848,7 +1909,7 @@ export class AgentManager {
|
|
|
1848
1909
|
agent.persistence = attachPersistenceCwd({ provider: agent.provider, sessionId: newInfo.sessionId }, agent.cwd);
|
|
1849
1910
|
}
|
|
1850
1911
|
// Emit state if runtimeInfo changed so clients get the updated model
|
|
1851
|
-
if (changed) {
|
|
1912
|
+
if (changed && options?.emit !== false) {
|
|
1852
1913
|
this.emitState(agent);
|
|
1853
1914
|
}
|
|
1854
1915
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentListItemPayload, AgentSnapshotPayload, RecentProviderSessionDescriptorPayload } from "../messages.js";
|
|
2
2
|
import type { StoredAgentRecord } from "./agent-storage.js";
|
|
3
|
-
import type { AgentProvider, AgentRuntimeInfo,
|
|
3
|
+
import type { AgentProvider, AgentRuntimeInfo, ImportableProviderSession } from "./agent-sdk-types.js";
|
|
4
4
|
import type { ManagedAgent } from "./agent-manager.js";
|
|
5
5
|
export type { ManagedAgent };
|
|
6
6
|
interface ProjectionOptions {
|
|
@@ -19,6 +19,8 @@ export declare function toStoredAgentRecord(agent: ManagedAgent, options?: Proje
|
|
|
19
19
|
export declare function toAgentPayload(agent: ManagedAgent, options?: ProjectionOptions): AgentSnapshotPayload;
|
|
20
20
|
export declare function buildStoredAgentPayload(record: StoredAgentRecord, validProviders: Iterable<AgentProvider>): AgentSnapshotPayload;
|
|
21
21
|
export declare function toAgentListItemPayload(agent: AgentSnapshotPayload): AgentListItemPayload;
|
|
22
|
-
export declare function toRecentProviderSessionDescriptorPayload(
|
|
22
|
+
export declare function toRecentProviderSessionDescriptorPayload(session: ImportableProviderSession & {
|
|
23
|
+
provider: string;
|
|
24
|
+
}, options: RecentProviderSessionProjectionOptions): RecentProviderSessionDescriptorPayload;
|
|
23
25
|
export declare function resolveStoredAgentPayloadUpdatedAt(record: StoredAgentRecord): string;
|
|
24
26
|
//# sourceMappingURL=agent-projections.d.ts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isStoredAgentProviderAvailable, toAgentPersistenceHandle } from "../persistence-hooks.js";
|
|
2
|
-
const PROMPT_PREVIEW_MAX_LENGTH = 160;
|
|
3
2
|
function normalizeThinkingOptionId(value) {
|
|
4
3
|
if (typeof value !== "string")
|
|
5
4
|
return null;
|
|
@@ -193,17 +192,16 @@ export function toAgentListItemPayload(agent) {
|
|
|
193
192
|
...(agent.providerUnavailable ? { providerUnavailable: true } : {}),
|
|
194
193
|
};
|
|
195
194
|
}
|
|
196
|
-
export function toRecentProviderSessionDescriptorPayload(
|
|
197
|
-
const promptPreviews = collectPromptPreviews(descriptor.timeline);
|
|
195
|
+
export function toRecentProviderSessionDescriptorPayload(session, options) {
|
|
198
196
|
return {
|
|
199
|
-
providerId:
|
|
197
|
+
providerId: session.provider,
|
|
200
198
|
providerLabel: options.providerLabel,
|
|
201
|
-
providerHandleId:
|
|
202
|
-
cwd:
|
|
203
|
-
title:
|
|
204
|
-
firstPromptPreview:
|
|
205
|
-
lastPromptPreview:
|
|
206
|
-
lastActivityAt:
|
|
199
|
+
providerHandleId: session.providerHandleId,
|
|
200
|
+
cwd: session.cwd,
|
|
201
|
+
title: session.title,
|
|
202
|
+
firstPromptPreview: session.firstPromptPreview,
|
|
203
|
+
lastPromptPreview: session.lastPromptPreview,
|
|
204
|
+
lastActivityAt: session.lastActivityAt.toISOString(),
|
|
207
205
|
};
|
|
208
206
|
}
|
|
209
207
|
export function resolveStoredAgentPayloadUpdatedAt(record) {
|
|
@@ -220,24 +218,6 @@ export function resolveStoredAgentPayloadUpdatedAt(record) {
|
|
|
220
218
|
timestamps.sort((a, b) => b.parsed - a.parsed);
|
|
221
219
|
return timestamps[0].raw;
|
|
222
220
|
}
|
|
223
|
-
function collectPromptPreviews(timeline) {
|
|
224
|
-
return timeline.flatMap((item) => {
|
|
225
|
-
if (item.type !== "user_message") {
|
|
226
|
-
return [];
|
|
227
|
-
}
|
|
228
|
-
const preview = normalizePromptPreview(item.text);
|
|
229
|
-
return preview ? [preview] : [];
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
function normalizePromptPreview(text) {
|
|
233
|
-
const normalized = text.trim().replace(/\s+/g, " ");
|
|
234
|
-
if (!normalized) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
return normalized.length > PROMPT_PREVIEW_MAX_LENGTH
|
|
238
|
-
? normalized.slice(0, PROMPT_PREVIEW_MAX_LENGTH)
|
|
239
|
-
: normalized;
|
|
240
|
-
}
|
|
241
221
|
function buildSerializableConfig(config) {
|
|
242
222
|
const serializable = {};
|
|
243
223
|
if (config.modeId) {
|
|
@@ -135,6 +135,7 @@ export type AgentFeature = AgentFeatureToggle | AgentFeatureSelect;
|
|
|
135
135
|
export interface AgentCapabilityFlags {
|
|
136
136
|
supportsStreaming: boolean;
|
|
137
137
|
supportsSessionPersistence: boolean;
|
|
138
|
+
supportsSessionListing?: boolean;
|
|
138
139
|
supportsDynamicModes: boolean;
|
|
139
140
|
supportsMcpServers: boolean;
|
|
140
141
|
supportsReasoningStream: boolean;
|
|
@@ -430,6 +431,7 @@ export interface AgentRuntimeInfo {
|
|
|
430
431
|
modeId?: string | null;
|
|
431
432
|
extra?: AgentMetadata;
|
|
432
433
|
}
|
|
434
|
+
export type AgentSlashCommandKind = "command" | "skill";
|
|
433
435
|
/**
|
|
434
436
|
* Represents a slash command available in an agent session.
|
|
435
437
|
* Commands are executed by sending them as prompts with / prefix.
|
|
@@ -438,25 +440,42 @@ export interface AgentSlashCommand {
|
|
|
438
440
|
name: string;
|
|
439
441
|
description: string;
|
|
440
442
|
argumentHint: string;
|
|
443
|
+
kind?: AgentSlashCommandKind;
|
|
441
444
|
}
|
|
442
|
-
export interface
|
|
445
|
+
export interface ListImportableSessionsOptions {
|
|
443
446
|
limit?: number;
|
|
444
447
|
/**
|
|
445
|
-
* Optional cwd hint. Providers that can cheaply pre-filter
|
|
446
|
-
* sessions by working directory should do so before doing expensive
|
|
447
|
-
* work like fetching turn timelines. Providers that can't filter
|
|
448
|
-
* cheaply may ignore this hint.
|
|
448
|
+
* Optional cwd hint. Providers that can cheaply pre-filter importable
|
|
449
|
+
* sessions by working directory should do so before doing expensive work.
|
|
449
450
|
*/
|
|
450
451
|
cwd?: string;
|
|
451
452
|
}
|
|
452
|
-
export interface
|
|
453
|
-
|
|
454
|
-
sessionId: string;
|
|
453
|
+
export interface ImportableProviderSession {
|
|
454
|
+
providerHandleId: string;
|
|
455
455
|
cwd: string;
|
|
456
456
|
title: string | null;
|
|
457
|
+
firstPromptPreview: string | null;
|
|
458
|
+
lastPromptPreview: string | null;
|
|
457
459
|
lastActivityAt: Date;
|
|
460
|
+
}
|
|
461
|
+
export interface ImportProviderSessionInput {
|
|
462
|
+
providerHandleId: string;
|
|
463
|
+
cwd: string;
|
|
464
|
+
}
|
|
465
|
+
export interface ImportProviderSessionContext {
|
|
466
|
+
config: AgentSessionConfig;
|
|
467
|
+
storedConfig: AgentSessionConfig;
|
|
468
|
+
launchContext?: AgentLaunchContext;
|
|
469
|
+
}
|
|
470
|
+
export interface ImportedTimelineEntry {
|
|
471
|
+
item: AgentTimelineItem;
|
|
472
|
+
timestamp?: string;
|
|
473
|
+
}
|
|
474
|
+
export interface ImportedProviderSession {
|
|
475
|
+
session: AgentSession;
|
|
476
|
+
config: AgentSessionConfig;
|
|
458
477
|
persistence: AgentPersistenceHandle;
|
|
459
|
-
timeline:
|
|
478
|
+
timeline: ImportedTimelineEntry[];
|
|
460
479
|
}
|
|
461
480
|
export interface AgentSessionConfig {
|
|
462
481
|
provider: AgentProvider;
|
|
@@ -575,7 +594,8 @@ export interface AgentClient {
|
|
|
575
594
|
isCreateConfigUnattended?(input: AgentCreateConfigUnattendedInput): boolean;
|
|
576
595
|
listCommands?(config: AgentSessionConfig): Promise<AgentSlashCommand[]>;
|
|
577
596
|
listFeatures?(config: AgentSessionConfig): Promise<AgentFeature[]>;
|
|
578
|
-
|
|
597
|
+
listImportableSessions?(options?: ListImportableSessionsOptions): Promise<ImportableProviderSession[]>;
|
|
598
|
+
importSession?(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<ImportedProviderSession>;
|
|
579
599
|
/**
|
|
580
600
|
* Check if this provider is available (CLI binary is installed).
|
|
581
601
|
* Returns true if available, false otherwise.
|
|
@@ -3,13 +3,14 @@ import type { Logger } from "pino";
|
|
|
3
3
|
import type { ProviderSnapshotManager } from "./provider-snapshot-manager.js";
|
|
4
4
|
import type { AgentManager, ManagedAgent } from "./agent-manager.js";
|
|
5
5
|
import type { AgentStorage } from "./agent-storage.js";
|
|
6
|
+
import type { AgentProvider } from "./agent-sdk-types.js";
|
|
6
7
|
import { scheduleAgentMetadataGeneration } from "./agent-metadata-generator.js";
|
|
7
8
|
import type { StructuredGenerationDaemonConfig } from "./structured-generation-providers.js";
|
|
8
9
|
import type { FetchRecentProviderSessionsRequestMessage, ImportAgentRequestMessageSchema, RecentProviderSessionDescriptorPayload } from "@getpaseo/protocol/messages";
|
|
9
10
|
import type { WorkspaceGitService } from "../workspace-git-service.js";
|
|
10
11
|
type ImportAgentRequestMessage = z.infer<typeof ImportAgentRequestMessageSchema>;
|
|
11
12
|
export interface NormalizedImportAgentRequest {
|
|
12
|
-
provider:
|
|
13
|
+
provider: AgentProvider;
|
|
13
14
|
providerHandleId: string;
|
|
14
15
|
cwd?: string;
|
|
15
16
|
labels?: Record<string, string>;
|
|
@@ -21,7 +22,7 @@ export declare class ImportSessionsRequestError extends Error {
|
|
|
21
22
|
}
|
|
22
23
|
export interface ListImportableProviderSessionsInput {
|
|
23
24
|
request: FetchRecentProviderSessionsRequestMessage;
|
|
24
|
-
agentManager: Pick<AgentManager, "listAgents" | "
|
|
25
|
+
agentManager: Pick<AgentManager, "listAgents" | "listImportableSessions">;
|
|
25
26
|
agentStorage: Pick<AgentStorage, "list">;
|
|
26
27
|
providerSnapshotManager: Pick<ProviderSnapshotManager, "getProviderLabel">;
|
|
27
28
|
}
|
|
@@ -23,7 +23,7 @@ export function normalizeImportAgentRequest(msg) {
|
|
|
23
23
|
return { error: "Import requires providerId and providerHandleId" };
|
|
24
24
|
}
|
|
25
25
|
return {
|
|
26
|
-
provider,
|
|
26
|
+
provider: provider,
|
|
27
27
|
providerHandleId,
|
|
28
28
|
cwd: msg.cwd,
|
|
29
29
|
labels: msg.labels,
|
|
@@ -36,7 +36,7 @@ export async function listImportableProviderSessions(input) {
|
|
|
36
36
|
const sinceTimestamp = parseRecentProviderSessionsSince(request.since);
|
|
37
37
|
const providerFilter = request.providers ? new Set(request.providers) : undefined;
|
|
38
38
|
const importedHandles = await collectImportedProviderSessionHandles(agentManager, agentStorage);
|
|
39
|
-
const
|
|
39
|
+
const sessions = await agentManager.listImportableSessions({
|
|
40
40
|
limit,
|
|
41
41
|
providerFilter,
|
|
42
42
|
cwd: request.cwd,
|
|
@@ -44,25 +44,21 @@ export async function listImportableProviderSessions(input) {
|
|
|
44
44
|
let filteredAlreadyImportedCount = 0;
|
|
45
45
|
const candidates = [];
|
|
46
46
|
const matchesRequestCwd = request.cwd ? createRealpathAwarePathMatcher(request.cwd) : null;
|
|
47
|
-
for (const
|
|
48
|
-
if (matchesRequestCwd && !matchesRequestCwd(
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (sinceTimestamp !== null && descriptor.lastActivityAt.getTime() < sinceTimestamp) {
|
|
47
|
+
for (const session of sessions) {
|
|
48
|
+
if (matchesRequestCwd && !matchesRequestCwd(session.cwd)) {
|
|
52
49
|
continue;
|
|
53
50
|
}
|
|
54
|
-
if (
|
|
51
|
+
if (sinceTimestamp !== null && session.lastActivityAt.getTime() < sinceTimestamp) {
|
|
55
52
|
continue;
|
|
56
53
|
}
|
|
57
|
-
if (
|
|
54
|
+
if (isMetadataGenerationSession(session)) {
|
|
58
55
|
continue;
|
|
59
56
|
}
|
|
60
|
-
|
|
61
|
-
if (importedHandles.has(toProviderSessionHandleKey(descriptor.provider, providerHandleId))) {
|
|
57
|
+
if (importedHandles.has(toProviderSessionHandleKey(session.provider, session.providerHandleId))) {
|
|
62
58
|
filteredAlreadyImportedCount += 1;
|
|
63
59
|
continue;
|
|
64
60
|
}
|
|
65
|
-
candidates.push(
|
|
61
|
+
candidates.push(session);
|
|
66
62
|
}
|
|
67
63
|
const entries = candidates
|
|
68
64
|
.sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime())
|
|
@@ -74,23 +70,19 @@ export async function listImportableProviderSessions(input) {
|
|
|
74
70
|
}
|
|
75
71
|
export async function importProviderSession(input) {
|
|
76
72
|
const { provider, providerHandleId, cwd, labels } = input.request;
|
|
77
|
-
|
|
78
|
-
cwd
|
|
79
|
-
});
|
|
80
|
-
if (!descriptor && provider === "opencode" && !cwd) {
|
|
81
|
-
throw new Error("OpenCode sessions require --cwd when the session cannot be found in persisted agents");
|
|
73
|
+
if (!cwd) {
|
|
74
|
+
throw new Error("Import requires cwd from the selected provider session");
|
|
82
75
|
}
|
|
83
|
-
const handle =
|
|
84
|
-
? applyImportCwdOverride(descriptor.persistence, cwd)
|
|
85
|
-
: buildImportPersistenceHandle({ provider, providerHandleId, cwd });
|
|
86
|
-
const overrides = cwd ? { cwd } : undefined;
|
|
76
|
+
const handle = buildImportPersistenceHandle({ provider, providerHandleId, cwd });
|
|
87
77
|
await unarchiveAgentByHandle(input.agentStorage, input.agentManager, handle);
|
|
88
|
-
const snapshot = await input.agentManager.
|
|
78
|
+
const snapshot = await input.agentManager.importProviderSession({
|
|
79
|
+
provider,
|
|
80
|
+
providerHandleId,
|
|
81
|
+
cwd,
|
|
89
82
|
labels,
|
|
90
83
|
});
|
|
91
84
|
await unarchiveAgentState(input.agentStorage, input.agentManager, snapshot.id);
|
|
92
|
-
|
|
93
|
-
await applyImportedAgentTitle({
|
|
85
|
+
scheduleImportedAgentMetadata({
|
|
94
86
|
snapshot,
|
|
95
87
|
agentManager: input.agentManager,
|
|
96
88
|
workspaceGitService: input.workspaceGitService,
|
|
@@ -108,24 +100,22 @@ export async function importProviderSession(input) {
|
|
|
108
100
|
async function unarchiveAgentByHandle(agentStorage, agentManager, handle) {
|
|
109
101
|
const records = await agentStorage.list();
|
|
110
102
|
const matched = records.find((record) => record.persistence?.provider === handle.provider &&
|
|
111
|
-
record.persistence
|
|
103
|
+
(record.persistence.sessionId === handle.sessionId ||
|
|
104
|
+
record.persistence.nativeHandle === handle.nativeHandle));
|
|
112
105
|
if (!matched) {
|
|
113
106
|
return;
|
|
114
107
|
}
|
|
115
108
|
await unarchiveAgentState(agentStorage, agentManager, matched.id);
|
|
116
109
|
}
|
|
117
|
-
|
|
110
|
+
function scheduleImportedAgentMetadata(input) {
|
|
118
111
|
const initialPrompt = getFirstUserMessageText(input.agentManager.getTimeline(input.snapshot.id));
|
|
119
112
|
if (!initialPrompt) {
|
|
120
113
|
return;
|
|
121
114
|
}
|
|
122
|
-
const { explicitTitle
|
|
115
|
+
const { explicitTitle } = resolveCreateAgentTitles({
|
|
123
116
|
configTitle: input.snapshot.config.title,
|
|
124
117
|
initialPrompt,
|
|
125
118
|
});
|
|
126
|
-
if (!explicitTitle && provisionalTitle) {
|
|
127
|
-
await input.agentManager.setTitle(input.snapshot.id, provisionalTitle);
|
|
128
|
-
}
|
|
129
119
|
input.scheduleAgentMetadataGeneration({
|
|
130
120
|
agentManager: input.agentManager,
|
|
131
121
|
agentId: input.snapshot.id,
|
|
@@ -157,27 +147,13 @@ function parseRecentProviderSessionsSince(since) {
|
|
|
157
147
|
return timestamp;
|
|
158
148
|
}
|
|
159
149
|
function buildImportPersistenceHandle(input) {
|
|
160
|
-
const cwd = input.cwd ?? process.cwd();
|
|
161
150
|
return {
|
|
162
151
|
provider: input.provider,
|
|
163
152
|
sessionId: input.providerHandleId,
|
|
164
153
|
nativeHandle: input.providerHandleId,
|
|
165
154
|
metadata: {
|
|
166
155
|
provider: input.provider,
|
|
167
|
-
cwd,
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
function applyImportCwdOverride(handle, cwd) {
|
|
172
|
-
if (!cwd) {
|
|
173
|
-
return handle;
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
...handle,
|
|
177
|
-
metadata: {
|
|
178
|
-
...handle.metadata,
|
|
179
|
-
provider: handle.provider,
|
|
180
|
-
cwd,
|
|
156
|
+
cwd: input.cwd,
|
|
181
157
|
},
|
|
182
158
|
};
|
|
183
159
|
}
|
|
@@ -206,16 +182,8 @@ async function collectImportedProviderSessionHandles(agentManager, agentStorage)
|
|
|
206
182
|
function toProviderSessionHandleKey(provider, providerHandleId) {
|
|
207
183
|
return `${provider}\0${providerHandleId}`;
|
|
208
184
|
}
|
|
209
|
-
function
|
|
210
|
-
|
|
211
|
-
if (item.type !== "user_message")
|
|
212
|
-
continue;
|
|
213
|
-
return item.text.trimStart().startsWith(METADATA_GENERATION_PROMPT_PREFIX);
|
|
214
|
-
}
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
function hasUserPrompt(descriptor) {
|
|
218
|
-
return descriptor.timeline.some((item) => item.type === "user_message" && item.text.trim() !== "");
|
|
185
|
+
function isMetadataGenerationSession(input) {
|
|
186
|
+
return (input.firstPromptPreview?.trimStart().startsWith(METADATA_GENERATION_PROMPT_PREFIX) ?? false);
|
|
219
187
|
}
|
|
220
188
|
function collectProviderSessionHandleKeys(target, provider, persistence) {
|
|
221
189
|
if (!persistence) {
|
|
@@ -141,16 +141,6 @@ function mapStreamEvent(provider, event) {
|
|
|
141
141
|
provider,
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
|
-
function mapPersistedAgentDescriptor(provider, descriptor) {
|
|
145
|
-
return {
|
|
146
|
-
...descriptor,
|
|
147
|
-
provider,
|
|
148
|
-
persistence: {
|
|
149
|
-
...descriptor.persistence,
|
|
150
|
-
provider,
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
144
|
function mapModel(provider, model) {
|
|
155
145
|
return normalizeAgentModelDefinition({ ...model, provider });
|
|
156
146
|
}
|
|
@@ -222,7 +212,8 @@ export function wrapSessionProvider(provider, inner) {
|
|
|
222
212
|
};
|
|
223
213
|
}
|
|
224
214
|
function wrapClientProvider(provider, inner, profileModels, additionalModels, profileModelsAreAdditive) {
|
|
225
|
-
const
|
|
215
|
+
const listImportableSessions = inner.listImportableSessions?.bind(inner);
|
|
216
|
+
const importSession = inner.importSession?.bind(inner);
|
|
226
217
|
return {
|
|
227
218
|
provider,
|
|
228
219
|
capabilities: inner.capabilities,
|
|
@@ -245,8 +236,36 @@ function wrapClientProvider(provider, inner, profileModels, additionalModels, pr
|
|
|
245
236
|
listModes: inner.listModes?.bind(inner),
|
|
246
237
|
resolveCreateConfig: inner.resolveCreateConfig?.bind(inner),
|
|
247
238
|
isCreateConfigUnattended: inner.isCreateConfigUnattended?.bind(inner),
|
|
248
|
-
|
|
249
|
-
? async (options) =>
|
|
239
|
+
listImportableSessions: listImportableSessions
|
|
240
|
+
? async (options) => await listImportableSessions(options)
|
|
241
|
+
: undefined,
|
|
242
|
+
importSession: importSession
|
|
243
|
+
? async (input, context) => {
|
|
244
|
+
const imported = await importSession(input, {
|
|
245
|
+
...context,
|
|
246
|
+
config: {
|
|
247
|
+
...context.config,
|
|
248
|
+
provider: inner.provider,
|
|
249
|
+
},
|
|
250
|
+
storedConfig: {
|
|
251
|
+
...context.storedConfig,
|
|
252
|
+
provider: inner.provider,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const persistence = mapPersistenceHandle(provider, imported.persistence);
|
|
256
|
+
if (!persistence) {
|
|
257
|
+
throw new Error(`Provider '${provider}' import did not return persistence`);
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
...imported,
|
|
261
|
+
session: wrapSessionProvider(provider, imported.session),
|
|
262
|
+
config: {
|
|
263
|
+
...imported.config,
|
|
264
|
+
provider,
|
|
265
|
+
},
|
|
266
|
+
persistence,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
250
269
|
: undefined,
|
|
251
270
|
isAvailable: () => inner.isAvailable(),
|
|
252
271
|
getDiagnostic: inner.getDiagnostic?.bind(inner),
|
|
@@ -352,6 +371,7 @@ function addDerivedProviders(resolvedProviders, providerOverrides) {
|
|
|
352
371
|
env: override.env,
|
|
353
372
|
providerId,
|
|
354
373
|
label: override.label ?? providerId,
|
|
374
|
+
providerParams: override.params,
|
|
355
375
|
})
|
|
356
376
|
: new GenericACPAgentClient({
|
|
357
377
|
logger,
|
|
@@ -359,6 +379,7 @@ function addDerivedProviders(resolvedProviders, providerOverrides) {
|
|
|
359
379
|
env: override.env,
|
|
360
380
|
providerId,
|
|
361
381
|
label: override.label ?? providerId,
|
|
382
|
+
providerParams: override.params,
|
|
362
383
|
}),
|
|
363
384
|
});
|
|
364
385
|
continue;
|