@getpaseo/server 0.1.93 → 0.1.95
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 +14 -0
- package/dist/server/server/agent/agent-manager.js +36 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts +1 -0
- package/dist/server/server/agent/providers/claude/agent.d.ts +1 -0
- package/dist/server/server/agent/providers/claude/agent.js +58 -3
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -2
- package/dist/server/server/agent/providers/mock-load-test-agent.js +55 -28
- package/dist/server/server/agent/providers/pi/agent.js +3 -1
- package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +5 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.js +74 -12
- package/dist/server/server/agent/runtime-mcp-config.d.ts +6 -0
- package/dist/server/server/agent/runtime-mcp-config.js +3 -0
- package/dist/server/server/auth.d.ts +13 -0
- package/dist/server/server/auth.js +35 -0
- package/dist/server/server/bootstrap.d.ts +2 -0
- package/dist/server/server/bootstrap.js +28 -1
- package/dist/server/server/config.js +3 -1
- package/dist/server/server/daemon-config-store.js +3 -0
- package/dist/server/server/loop-service.d.ts +6 -6
- package/dist/server/server/persisted-config.d.ts +61 -0
- package/dist/server/server/persisted-config.js +2 -0
- package/dist/server/server/session.d.ts +6 -0
- package/dist/server/server/session.js +115 -7
- package/dist/server/server/websocket-server.js +2 -0
- package/dist/server/server/workspace-git-service.d.ts +4 -1
- package/dist/server/server/workspace-git-service.js +40 -22
- package/dist/server/server/workspace-reconciliation-service.js +10 -5
- package/dist/server/services/github-service.d.ts +57 -0
- package/dist/server/services/github-service.js +434 -5
- package/dist/server/terminal/terminal-session-controller.d.ts +6 -0
- package/dist/server/terminal/terminal-session-controller.js +36 -2
- package/dist/src/server/persisted-config.js +2 -0
- package/package.json +5 -5
|
@@ -19,6 +19,7 @@ import { AgentStorage } from "./agent/agent-storage.js";
|
|
|
19
19
|
import type { TerminalManager } from "../terminal/terminal-manager.js";
|
|
20
20
|
import type { PushNotificationSender } from "./push/notifications.js";
|
|
21
21
|
import type { AgentClient, AgentProvider } from "./agent/agent-sdk-types.js";
|
|
22
|
+
import type { TerminalProfile } from "@getpaseo/protocol/messages";
|
|
22
23
|
import type { AgentProviderRuntimeSettingsMap, ProviderOverride } from "./agent/provider-launch-config.js";
|
|
23
24
|
import type { PersistedConfig } from "./persisted-config.js";
|
|
24
25
|
import { type ServiceProxySubsystem } from "./service-proxy.js";
|
|
@@ -57,6 +58,7 @@ export interface PaseoDaemonConfig {
|
|
|
57
58
|
mcpInjectIntoAgents?: boolean;
|
|
58
59
|
autoArchiveAfterMerge?: boolean;
|
|
59
60
|
appendSystemPrompt?: string;
|
|
61
|
+
terminalProfiles?: TerminalProfile[];
|
|
60
62
|
staticDir: string;
|
|
61
63
|
mcpDebug: boolean;
|
|
62
64
|
isDev?: boolean;
|
|
@@ -104,7 +104,7 @@ import { ScriptHealthMonitor } from "./script-health-monitor.js";
|
|
|
104
104
|
import { createScriptStatusEmitter } from "./script-status-projection.js";
|
|
105
105
|
import { WorkspaceScriptRuntimeStore } from "./workspace-script-runtime-store.js";
|
|
106
106
|
import { isHostnameAllowed } from "./hostnames.js";
|
|
107
|
-
import { createRequireBearerMiddleware } from "./auth.js";
|
|
107
|
+
import { createRequireBearerMiddleware, isAgentMcpRequestAuthorized, } from "./auth.js";
|
|
108
108
|
const MAX_MCP_DEBUG_BATCH_ITEMS = 10;
|
|
109
109
|
const REDACTED_LOG_VALUE = "[redacted]";
|
|
110
110
|
const DOWNLOAD_OPEN_FLAGS = process.platform === "win32" ? constants.O_RDONLY : constants.O_RDONLY | constants.O_NOFOLLOW;
|
|
@@ -174,6 +174,9 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
174
174
|
},
|
|
175
175
|
autoArchiveAfterMerge: config.autoArchiveAfterMerge ?? false,
|
|
176
176
|
appendSystemPrompt: config.appendSystemPrompt ?? "",
|
|
177
|
+
...(config.terminalProfiles !== undefined
|
|
178
|
+
? { terminalProfiles: config.terminalProfiles }
|
|
179
|
+
: {}),
|
|
177
180
|
}, logger);
|
|
178
181
|
const serverId = getOrCreateServerId(config.paseoHome, { logger });
|
|
179
182
|
const daemonKeyPair = await loadOrCreateDaemonKeyPair(config.paseoHome, logger);
|
|
@@ -183,6 +186,14 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
183
186
|
const downloadTokenStore = new DownloadTokenStore({
|
|
184
187
|
ttlMs: downloadTokenTtlMs,
|
|
185
188
|
});
|
|
189
|
+
// Capability token authenticating the daemon's own agents to the loopback
|
|
190
|
+
// Agent MCP endpoint (/mcp/agents). Random per daemon run, injected only into
|
|
191
|
+
// local agent configs and the daemon's own MCP client — never sent to remote
|
|
192
|
+
// clients — so it cannot be replayed off-box. This lets the injected MCP
|
|
193
|
+
// authenticate even when the daemon password is set via the app (hash only,
|
|
194
|
+
// no plaintext available). Mirrors the /api/files/download capability-token
|
|
195
|
+
// pattern.
|
|
196
|
+
const agentMcpAuthToken = randomUUID();
|
|
186
197
|
const listenTarget = parseListenString(config.listen);
|
|
187
198
|
const app = express();
|
|
188
199
|
let boundListenTarget = null;
|
|
@@ -375,6 +386,10 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
375
386
|
providerDefinitions: initialAgentManagerState.providerDefinitions,
|
|
376
387
|
registry: agentStorage,
|
|
377
388
|
appendSystemPrompt: config.appendSystemPrompt,
|
|
389
|
+
onWorkspaceStateMayHaveChanged: ({ cwd }) => {
|
|
390
|
+
workspaceGitService.onWorkspaceStateMayHaveChanged(cwd);
|
|
391
|
+
},
|
|
392
|
+
mcpAuthToken: agentMcpAuthToken,
|
|
378
393
|
logger,
|
|
379
394
|
});
|
|
380
395
|
const detachAgentStoragePersistence = attachAgentStoragePersistence(logger, agentManager, agentStorage);
|
|
@@ -586,6 +601,18 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
586
601
|
return transport;
|
|
587
602
|
};
|
|
588
603
|
const runAgentMcpRequest = async (req, res) => {
|
|
604
|
+
// This route is exempt from the global daemon-password middleware, so it
|
|
605
|
+
// authenticates here using the injected capability token (or a valid
|
|
606
|
+
// daemon password). Without this, a password-protected daemon would be
|
|
607
|
+
// wide open on its agent control plane.
|
|
608
|
+
if (!(await isAgentMcpRequestAuthorized({
|
|
609
|
+
password: config.auth?.password,
|
|
610
|
+
capabilityToken: agentMcpAuthToken,
|
|
611
|
+
authorizationHeader: req.header("authorization"),
|
|
612
|
+
}))) {
|
|
613
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
589
616
|
if (config.mcpDebug) {
|
|
590
617
|
logger.debug({
|
|
591
618
|
method: req.method,
|
|
@@ -192,6 +192,7 @@ function resolveStaticLoadConfigSettings(env, cli, persisted) {
|
|
|
192
192
|
mcpInjectIntoAgents: cli?.mcpInjectIntoAgents ?? persisted.daemon?.mcp?.injectIntoAgents ?? false,
|
|
193
193
|
autoArchiveAfterMerge: persisted.daemon?.autoArchiveAfterMerge ?? false,
|
|
194
194
|
appendSystemPrompt: resolveAppendSystemPrompt(persisted),
|
|
195
|
+
terminalProfiles: persisted.daemon?.terminalProfiles,
|
|
195
196
|
hostnames: mergeHostnames([
|
|
196
197
|
persisted.daemon?.hostnames,
|
|
197
198
|
parseHostnamesEnv(env.PASEO_HOSTNAMES ?? env.PASEO_ALLOWED_HOSTS),
|
|
@@ -204,7 +205,7 @@ export function loadConfig(paseoHome, options) {
|
|
|
204
205
|
const env = options?.env ?? process.env;
|
|
205
206
|
const persisted = loadPersistedConfig(paseoHome);
|
|
206
207
|
const listen = resolveListenAddress(env, options?.cli, persisted);
|
|
207
|
-
const { mcpEnabled, mcpInjectIntoAgents, autoArchiveAfterMerge, appendSystemPrompt, hostnames, appBaseUrl, } = resolveStaticLoadConfigSettings(env, options?.cli, persisted);
|
|
208
|
+
const { mcpEnabled, mcpInjectIntoAgents, autoArchiveAfterMerge, appendSystemPrompt, terminalProfiles, hostnames, appBaseUrl, } = resolveStaticLoadConfigSettings(env, options?.cli, persisted);
|
|
208
209
|
const relay = resolveRelayConfig({
|
|
209
210
|
env,
|
|
210
211
|
persisted,
|
|
@@ -229,6 +230,7 @@ export function loadConfig(paseoHome, options) {
|
|
|
229
230
|
mcpInjectIntoAgents,
|
|
230
231
|
autoArchiveAfterMerge,
|
|
231
232
|
appendSystemPrompt,
|
|
233
|
+
terminalProfiles,
|
|
232
234
|
mcpDebug: env.MCP_DEBUG === "1",
|
|
233
235
|
isDev: resolvePaseoNodeEnv(env) === "development",
|
|
234
236
|
agentStoragePath: path.join(paseoHome, "agents"),
|
|
@@ -147,6 +147,9 @@ function mergeMutableConfigIntoPersistedConfig(params) {
|
|
|
147
147
|
},
|
|
148
148
|
autoArchiveAfterMerge: mutable.autoArchiveAfterMerge,
|
|
149
149
|
appendSystemPrompt: mutable.appendSystemPrompt,
|
|
150
|
+
...(mutable.terminalProfiles !== undefined
|
|
151
|
+
? { terminalProfiles: mutable.terminalProfiles }
|
|
152
|
+
: {}),
|
|
150
153
|
},
|
|
151
154
|
agents: nextAgents,
|
|
152
155
|
};
|
|
@@ -15,14 +15,14 @@ declare const LoopLogEntrySchema: z.ZodObject<{
|
|
|
15
15
|
level: "error" | "info";
|
|
16
16
|
seq: number;
|
|
17
17
|
timestamp: string;
|
|
18
|
-
source: "
|
|
18
|
+
source: "loop" | "worker" | "verifier" | "verify-check";
|
|
19
19
|
iteration: number | null;
|
|
20
20
|
}, {
|
|
21
21
|
text: string;
|
|
22
22
|
level: "error" | "info";
|
|
23
23
|
seq: number;
|
|
24
24
|
timestamp: string;
|
|
25
|
-
source: "
|
|
25
|
+
source: "loop" | "worker" | "verifier" | "verify-check";
|
|
26
26
|
iteration: number | null;
|
|
27
27
|
}>;
|
|
28
28
|
declare const LoopVerifyCheckResultSchema: z.ZodObject<{
|
|
@@ -314,14 +314,14 @@ declare const LoopRecordSchema: z.ZodObject<{
|
|
|
314
314
|
level: "error" | "info";
|
|
315
315
|
seq: number;
|
|
316
316
|
timestamp: string;
|
|
317
|
-
source: "
|
|
317
|
+
source: "loop" | "worker" | "verifier" | "verify-check";
|
|
318
318
|
iteration: number | null;
|
|
319
319
|
}, {
|
|
320
320
|
text: string;
|
|
321
321
|
level: "error" | "info";
|
|
322
322
|
seq: number;
|
|
323
323
|
timestamp: string;
|
|
324
|
-
source: "
|
|
324
|
+
source: "loop" | "worker" | "verifier" | "verify-check";
|
|
325
325
|
iteration: number | null;
|
|
326
326
|
}>, "many">;
|
|
327
327
|
nextLogSeq: z.ZodNumber;
|
|
@@ -384,7 +384,7 @@ declare const LoopRecordSchema: z.ZodObject<{
|
|
|
384
384
|
level: "error" | "info";
|
|
385
385
|
seq: number;
|
|
386
386
|
timestamp: string;
|
|
387
|
-
source: "
|
|
387
|
+
source: "loop" | "worker" | "verifier" | "verify-check";
|
|
388
388
|
iteration: number | null;
|
|
389
389
|
}[];
|
|
390
390
|
nextLogSeq: number;
|
|
@@ -445,7 +445,7 @@ declare const LoopRecordSchema: z.ZodObject<{
|
|
|
445
445
|
level: "error" | "info";
|
|
446
446
|
seq: number;
|
|
447
447
|
timestamp: string;
|
|
448
|
-
source: "
|
|
448
|
+
source: "loop" | "worker" | "verifier" | "verify-check";
|
|
449
449
|
iteration: number | null;
|
|
450
450
|
}[];
|
|
451
451
|
nextLogSeq: number;
|
|
@@ -21,6 +21,25 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
21
21
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
22
22
|
autoArchiveAfterMerge: z.ZodOptional<z.ZodBoolean>;
|
|
23
23
|
appendSystemPrompt: z.ZodOptional<z.ZodString>;
|
|
24
|
+
terminalProfiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
name: z.ZodString;
|
|
27
|
+
command: z.ZodString;
|
|
28
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
29
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
30
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
31
|
+
id: z.ZodString;
|
|
32
|
+
name: z.ZodString;
|
|
33
|
+
command: z.ZodString;
|
|
34
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
35
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
37
|
+
id: z.ZodString;
|
|
38
|
+
name: z.ZodString;
|
|
39
|
+
command: z.ZodString;
|
|
40
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
41
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
42
|
+
}, z.ZodTypeAny, "passthrough">>, "many">>;
|
|
24
43
|
cors: z.ZodOptional<z.ZodObject<{
|
|
25
44
|
allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
26
45
|
}, "strict", z.ZodTypeAny, {
|
|
@@ -80,6 +99,13 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
80
99
|
allowedHosts?: true | string[] | undefined;
|
|
81
100
|
autoArchiveAfterMerge?: boolean | undefined;
|
|
82
101
|
appendSystemPrompt?: string | undefined;
|
|
102
|
+
terminalProfiles?: z.objectOutputType<{
|
|
103
|
+
id: z.ZodString;
|
|
104
|
+
name: z.ZodString;
|
|
105
|
+
command: z.ZodString;
|
|
106
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
107
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
108
|
+
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
83
109
|
cors?: {
|
|
84
110
|
allowedOrigins?: string[] | undefined;
|
|
85
111
|
} | undefined;
|
|
@@ -108,6 +134,13 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
108
134
|
allowedHosts?: true | string[] | undefined;
|
|
109
135
|
autoArchiveAfterMerge?: boolean | undefined;
|
|
110
136
|
appendSystemPrompt?: string | undefined;
|
|
137
|
+
terminalProfiles?: z.objectInputType<{
|
|
138
|
+
id: z.ZodString;
|
|
139
|
+
name: z.ZodString;
|
|
140
|
+
command: z.ZodString;
|
|
141
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
142
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
143
|
+
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
111
144
|
cors?: {
|
|
112
145
|
allowedOrigins?: string[] | undefined;
|
|
113
146
|
} | undefined;
|
|
@@ -135,6 +168,13 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
135
168
|
hostnames?: true | string[] | undefined;
|
|
136
169
|
autoArchiveAfterMerge?: boolean | undefined;
|
|
137
170
|
appendSystemPrompt?: string | undefined;
|
|
171
|
+
terminalProfiles?: z.objectOutputType<{
|
|
172
|
+
id: z.ZodString;
|
|
173
|
+
name: z.ZodString;
|
|
174
|
+
command: z.ZodString;
|
|
175
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
176
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
177
|
+
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
138
178
|
cors?: {
|
|
139
179
|
allowedOrigins?: string[] | undefined;
|
|
140
180
|
} | undefined;
|
|
@@ -163,6 +203,13 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
163
203
|
allowedHosts?: true | string[] | undefined;
|
|
164
204
|
autoArchiveAfterMerge?: boolean | undefined;
|
|
165
205
|
appendSystemPrompt?: string | undefined;
|
|
206
|
+
terminalProfiles?: z.objectInputType<{
|
|
207
|
+
id: z.ZodString;
|
|
208
|
+
name: z.ZodString;
|
|
209
|
+
command: z.ZodString;
|
|
210
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
211
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
212
|
+
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
166
213
|
cors?: {
|
|
167
214
|
allowedOrigins?: string[] | undefined;
|
|
168
215
|
} | undefined;
|
|
@@ -853,6 +900,13 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
853
900
|
hostnames?: true | string[] | undefined;
|
|
854
901
|
autoArchiveAfterMerge?: boolean | undefined;
|
|
855
902
|
appendSystemPrompt?: string | undefined;
|
|
903
|
+
terminalProfiles?: z.objectOutputType<{
|
|
904
|
+
id: z.ZodString;
|
|
905
|
+
name: z.ZodString;
|
|
906
|
+
command: z.ZodString;
|
|
907
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
908
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
909
|
+
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
856
910
|
cors?: {
|
|
857
911
|
allowedOrigins?: string[] | undefined;
|
|
858
912
|
} | undefined;
|
|
@@ -992,6 +1046,13 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
992
1046
|
allowedHosts?: true | string[] | undefined;
|
|
993
1047
|
autoArchiveAfterMerge?: boolean | undefined;
|
|
994
1048
|
appendSystemPrompt?: string | undefined;
|
|
1049
|
+
terminalProfiles?: z.objectInputType<{
|
|
1050
|
+
id: z.ZodString;
|
|
1051
|
+
name: z.ZodString;
|
|
1052
|
+
command: z.ZodString;
|
|
1053
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1054
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
1055
|
+
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
995
1056
|
cors?: {
|
|
996
1057
|
allowedOrigins?: string[] | undefined;
|
|
997
1058
|
} | undefined;
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { AgentProviderRuntimeSettingsMapSchema, migrateProviderSettings, ProviderOverridesSchema, } from "./agent/provider-launch-config.js";
|
|
5
5
|
import { ensurePrivateFile, writePrivateFileAtomicSync } from "./private-files.js";
|
|
6
|
+
import { TerminalProfileSchema } from "@getpaseo/protocol/messages";
|
|
6
7
|
export const LogLevelSchema = z.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
|
|
7
8
|
export const LogFormatSchema = z.enum(["pretty", "json"]);
|
|
8
9
|
const LogConfigSchema = z
|
|
@@ -187,6 +188,7 @@ export const PersistedConfigSchema = z
|
|
|
187
188
|
.optional(),
|
|
188
189
|
autoArchiveAfterMerge: z.boolean().optional(),
|
|
189
190
|
appendSystemPrompt: z.string().optional(),
|
|
191
|
+
terminalProfiles: z.array(TerminalProfileSchema).optional(),
|
|
190
192
|
cors: z
|
|
191
193
|
.object({
|
|
192
194
|
allowedOrigins: z.array(z.string()).optional(),
|
|
@@ -34,6 +34,9 @@ export interface SessionRuntimeMetrics {
|
|
|
34
34
|
inflightRequests: number;
|
|
35
35
|
peakInflightRequests: number;
|
|
36
36
|
}
|
|
37
|
+
export interface SessionFileSystem {
|
|
38
|
+
isDirectory(path: string): Promise<boolean>;
|
|
39
|
+
}
|
|
37
40
|
type AgentMcpTransportFactory = () => Promise<unknown>;
|
|
38
41
|
export interface SessionOptions {
|
|
39
42
|
clientId: string;
|
|
@@ -51,6 +54,7 @@ export interface SessionOptions {
|
|
|
51
54
|
agentStorage: AgentStorage;
|
|
52
55
|
projectRegistry: ProjectRegistry;
|
|
53
56
|
workspaceRegistry: WorkspaceRegistry;
|
|
57
|
+
filesystem?: SessionFileSystem;
|
|
54
58
|
chatService: FileBackedChatService;
|
|
55
59
|
scheduleService: ScheduleService;
|
|
56
60
|
loopService: LoopService;
|
|
@@ -149,6 +153,7 @@ export declare class Session {
|
|
|
149
153
|
private readonly agentStorage;
|
|
150
154
|
private readonly projectRegistry;
|
|
151
155
|
private readonly workspaceRegistry;
|
|
156
|
+
private readonly filesystem;
|
|
152
157
|
private readonly chatService;
|
|
153
158
|
private readonly scheduleService;
|
|
154
159
|
private readonly loopService;
|
|
@@ -415,6 +420,7 @@ export declare class Session {
|
|
|
415
420
|
private resolveCurrentPullRequest;
|
|
416
421
|
private handleCheckoutPrStatusRequest;
|
|
417
422
|
private handlePullRequestTimelineRequest;
|
|
423
|
+
private handleCheckoutGithubGetCheckDetailsRequest;
|
|
418
424
|
private handlePaseoWorktreeListRequest;
|
|
419
425
|
private handlePaseoWorktreeArchiveRequest;
|
|
420
426
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import equal from "fast-deep-equal";
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
|
+
import { stat } from "node:fs/promises";
|
|
4
5
|
import { basename, resolve, sep } from "path";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
import { z } from "zod";
|
|
@@ -215,6 +216,12 @@ const PCM_BYTES_PER_MS = (PCM_SAMPLE_RATE * PCM_CHANNELS * (PCM_BITS_PER_SAMPLE
|
|
|
215
216
|
const MIN_STREAMING_SEGMENT_DURATION_MS = 1000;
|
|
216
217
|
const MIN_STREAMING_SEGMENT_BYTES = Math.round(PCM_BYTES_PER_MS * MIN_STREAMING_SEGMENT_DURATION_MS);
|
|
217
218
|
const AgentIdSchema = z.string().uuid();
|
|
219
|
+
const nodeSessionFileSystem = {
|
|
220
|
+
async isDirectory(path) {
|
|
221
|
+
const stats = await stat(path).catch(() => null);
|
|
222
|
+
return stats?.isDirectory() ?? false;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
218
225
|
class VoiceFeatureUnavailableError extends Error {
|
|
219
226
|
constructor(context) {
|
|
220
227
|
super(context.message);
|
|
@@ -258,6 +265,12 @@ function parseClientCapabilities(capabilities) {
|
|
|
258
265
|
}
|
|
259
266
|
return new Set(result);
|
|
260
267
|
}
|
|
268
|
+
function describeRegistryTransition(record) {
|
|
269
|
+
if (!record) {
|
|
270
|
+
return "created";
|
|
271
|
+
}
|
|
272
|
+
return record.archivedAt ? "unarchived" : "existing";
|
|
273
|
+
}
|
|
261
274
|
/**
|
|
262
275
|
* Session represents a single connected client session.
|
|
263
276
|
* It owns all state management, orchestration logic, and message processing.
|
|
@@ -318,7 +331,7 @@ export class Session {
|
|
|
318
331
|
}
|
|
319
332
|
},
|
|
320
333
|
});
|
|
321
|
-
const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, worktreesRoot, agentManager, agentStorage, projectRegistry, workspaceRegistry, chatService, scheduleService, loopService, checkoutDiffManager, github, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, sttLanguage, tts, terminalManager, providerSnapshotManager, serviceProxy, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, serviceProxyPublicBaseUrl, resolveScriptHealth, voice, voiceBridge, dictation, serverId, daemonVersion, daemonRuntimeConfig, } = options;
|
|
334
|
+
const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, worktreesRoot, agentManager, agentStorage, projectRegistry, workspaceRegistry, filesystem, chatService, scheduleService, loopService, checkoutDiffManager, github, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, sttLanguage, tts, terminalManager, providerSnapshotManager, serviceProxy, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, serviceProxyPublicBaseUrl, resolveScriptHealth, voice, voiceBridge, dictation, serverId, daemonVersion, daemonRuntimeConfig, } = options;
|
|
322
335
|
this.clientId = clientId;
|
|
323
336
|
this.appVersion = appVersion ?? null;
|
|
324
337
|
this.clientCapabilities = parseClientCapabilities(clientCapabilities);
|
|
@@ -340,6 +353,7 @@ export class Session {
|
|
|
340
353
|
this.agentStorage = agentStorage;
|
|
341
354
|
this.projectRegistry = projectRegistry;
|
|
342
355
|
this.workspaceRegistry = workspaceRegistry;
|
|
356
|
+
this.filesystem = filesystem ?? nodeSessionFileSystem;
|
|
343
357
|
this.chatService = chatService;
|
|
344
358
|
this.scheduleService = scheduleService;
|
|
345
359
|
this.loopService = loopService;
|
|
@@ -356,6 +370,12 @@ export class Session {
|
|
|
356
370
|
hasBinaryChannel: () => this.onBinaryMessage !== null,
|
|
357
371
|
isPathWithinRoot: (rootPath, candidatePath) => this.isPathWithinRoot(rootPath, candidatePath),
|
|
358
372
|
sessionLogger: this.sessionLogger,
|
|
373
|
+
listTerminalWorkspaceRoots: async () => {
|
|
374
|
+
const workspaces = await this.workspaceRegistry.list();
|
|
375
|
+
return workspaces
|
|
376
|
+
.filter((workspace) => !workspace.archivedAt)
|
|
377
|
+
.map((workspace) => workspace.cwd);
|
|
378
|
+
},
|
|
359
379
|
clientSupportsWrapReflow: () => this.clientCapabilities.has(CLIENT_CAPS.terminalReflowableSnapshot),
|
|
360
380
|
});
|
|
361
381
|
this.createAgentLifecycleDispatch = new CreateAgentLifecycleDispatch({
|
|
@@ -586,7 +606,10 @@ export class Session {
|
|
|
586
606
|
this.sessionLogger.info("Skipping Agent MCP initialization because no MCP base URL is configured");
|
|
587
607
|
return;
|
|
588
608
|
}
|
|
589
|
-
const
|
|
609
|
+
const authToken = this.agentManager.getMcpAuthToken();
|
|
610
|
+
const transport = new StreamableHTTPClientTransport(new URL(this.mcpBaseUrl), authToken
|
|
611
|
+
? { requestInit: { headers: { Authorization: `Bearer ${authToken}` } } }
|
|
612
|
+
: undefined);
|
|
590
613
|
this.agentMcpClient = await experimental_createMCPClient({
|
|
591
614
|
transport,
|
|
592
615
|
});
|
|
@@ -1279,6 +1302,8 @@ export class Session {
|
|
|
1279
1302
|
return this.handleCheckoutPrMergeRequest(msg);
|
|
1280
1303
|
case "checkout.github.set_auto_merge.request":
|
|
1281
1304
|
return this.handleCheckoutGithubSetAutoMergeRequest(msg);
|
|
1305
|
+
case "checkout.github.get_check_details.request":
|
|
1306
|
+
return this.handleCheckoutGithubGetCheckDetailsRequest(msg);
|
|
1282
1307
|
case "checkout_pr_status_request":
|
|
1283
1308
|
return this.handleCheckoutPrStatusRequest(msg);
|
|
1284
1309
|
case "pull_request_timeline_request":
|
|
@@ -4276,6 +4301,43 @@ export class Session {
|
|
|
4276
4301
|
});
|
|
4277
4302
|
}
|
|
4278
4303
|
}
|
|
4304
|
+
async handleCheckoutGithubGetCheckDetailsRequest(msg) {
|
|
4305
|
+
const { cwd, repoOwner, repoName, checkRunId, workflowRunId, requestId } = msg;
|
|
4306
|
+
try {
|
|
4307
|
+
const details = await this.github.getGitHubCheckDetails({
|
|
4308
|
+
cwd,
|
|
4309
|
+
repoOwner,
|
|
4310
|
+
repoName,
|
|
4311
|
+
checkRunId,
|
|
4312
|
+
workflowRunId,
|
|
4313
|
+
});
|
|
4314
|
+
this.emit({
|
|
4315
|
+
type: "checkout.github.get_check_details.response",
|
|
4316
|
+
payload: {
|
|
4317
|
+
cwd,
|
|
4318
|
+
success: true,
|
|
4319
|
+
details,
|
|
4320
|
+
error: null,
|
|
4321
|
+
requestId,
|
|
4322
|
+
},
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
catch (error) {
|
|
4326
|
+
this.emit({
|
|
4327
|
+
type: "checkout.github.get_check_details.response",
|
|
4328
|
+
payload: {
|
|
4329
|
+
cwd,
|
|
4330
|
+
success: false,
|
|
4331
|
+
details: null,
|
|
4332
|
+
error: {
|
|
4333
|
+
code: "UNKNOWN",
|
|
4334
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4335
|
+
},
|
|
4336
|
+
requestId,
|
|
4337
|
+
},
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4279
4341
|
async handlePaseoWorktreeListRequest(msg) {
|
|
4280
4342
|
return handleWorktreeListRequest({
|
|
4281
4343
|
emit: (message) => this.emit(message),
|
|
@@ -5061,9 +5123,10 @@ export class Session {
|
|
|
5061
5123
|
return result;
|
|
5062
5124
|
}
|
|
5063
5125
|
async archiveWorkspaceRecord(workspaceId, archivedAt) {
|
|
5126
|
+
const archiveTimestamp = archivedAt ?? new Date().toISOString();
|
|
5064
5127
|
const existingWorkspace = await archivePersistedWorkspaceRecord({
|
|
5065
5128
|
workspaceId,
|
|
5066
|
-
archivedAt,
|
|
5129
|
+
archivedAt: archiveTimestamp,
|
|
5067
5130
|
workspaceRegistry: this.workspaceRegistry,
|
|
5068
5131
|
projectRegistry: this.projectRegistry,
|
|
5069
5132
|
});
|
|
@@ -5071,6 +5134,16 @@ export class Session {
|
|
|
5071
5134
|
this.removeWorkspaceGitSubscription(workspaceId);
|
|
5072
5135
|
return;
|
|
5073
5136
|
}
|
|
5137
|
+
if (!existingWorkspace.archivedAt) {
|
|
5138
|
+
const activeSiblings = (await this.workspaceRegistry.list()).filter((workspace) => workspace.projectId === existingWorkspace.projectId && !workspace.archivedAt);
|
|
5139
|
+
this.sessionLogger.info({
|
|
5140
|
+
workspaceId,
|
|
5141
|
+
workspaceCwd: existingWorkspace.cwd,
|
|
5142
|
+
projectId: existingWorkspace.projectId,
|
|
5143
|
+
projectArchived: activeSiblings.length === 0,
|
|
5144
|
+
archivedAt: archiveTimestamp,
|
|
5145
|
+
}, "Workspace archived");
|
|
5146
|
+
}
|
|
5074
5147
|
await this.removeWorkspaceGitWatchTarget(existingWorkspace.cwd);
|
|
5075
5148
|
this.scriptRuntimeStore?.removeForWorkspace(existingWorkspace.cwd);
|
|
5076
5149
|
this.removeWorkspaceGitSubscription(workspaceId);
|
|
@@ -5389,11 +5462,47 @@ export class Session {
|
|
|
5389
5462
|
}
|
|
5390
5463
|
}
|
|
5391
5464
|
async handleOpenProjectRequest(request) {
|
|
5465
|
+
const requestedCwd = request.cwd;
|
|
5466
|
+
const cwd = expandTilde(requestedCwd);
|
|
5467
|
+
const directoryExists = await this.filesystem.isDirectory(cwd).catch(() => false);
|
|
5468
|
+
if (!directoryExists) {
|
|
5469
|
+
this.sessionLogger.info({ requestedCwd, resolvedCwd: cwd, reason: "directory_not_found" }, "Open project rejected");
|
|
5470
|
+
this.emit({
|
|
5471
|
+
type: "open_project_response",
|
|
5472
|
+
payload: {
|
|
5473
|
+
requestId: request.requestId,
|
|
5474
|
+
workspace: null,
|
|
5475
|
+
error: `Directory not found: ${cwd}`,
|
|
5476
|
+
errorCode: "directory_not_found",
|
|
5477
|
+
},
|
|
5478
|
+
});
|
|
5479
|
+
return;
|
|
5480
|
+
}
|
|
5392
5481
|
try {
|
|
5393
|
-
const
|
|
5482
|
+
const projectsBefore = new Map();
|
|
5483
|
+
for (const project of await this.projectRegistry.list()) {
|
|
5484
|
+
projectsBefore.set(project.projectId, project);
|
|
5485
|
+
}
|
|
5486
|
+
const workspacesBefore = new Map();
|
|
5487
|
+
for (const workspaceRecord of await this.workspaceRegistry.list()) {
|
|
5488
|
+
workspacesBefore.set(workspaceRecord.workspaceId, workspaceRecord);
|
|
5489
|
+
}
|
|
5490
|
+
const workspace = await this.findOrCreateWorkspaceForDirectory(cwd);
|
|
5491
|
+
const project = await this.projectRegistry.get(workspace.projectId);
|
|
5394
5492
|
await this.syncWorkspaceGitObserverForWorkspace(workspace);
|
|
5395
5493
|
const descriptor = await this.describeWorkspaceRecord(workspace);
|
|
5396
5494
|
await this.emitWorkspaceUpdateForCwd(workspace.cwd);
|
|
5495
|
+
this.sessionLogger.info({
|
|
5496
|
+
requestedCwd,
|
|
5497
|
+
resolvedCwd: cwd,
|
|
5498
|
+
workspaceCwd: workspace.cwd,
|
|
5499
|
+
workspaceId: workspace.workspaceId,
|
|
5500
|
+
workspaceKind: workspace.kind,
|
|
5501
|
+
workspaceTransition: describeRegistryTransition(workspacesBefore.get(workspace.workspaceId) ?? null),
|
|
5502
|
+
projectId: workspace.projectId,
|
|
5503
|
+
projectKind: project?.kind ?? null,
|
|
5504
|
+
projectTransition: describeRegistryTransition(projectsBefore.get(workspace.projectId) ?? null),
|
|
5505
|
+
}, "Project opened");
|
|
5397
5506
|
this.emit({
|
|
5398
5507
|
type: "open_project_response",
|
|
5399
5508
|
payload: {
|
|
@@ -5414,7 +5523,7 @@ export class Session {
|
|
|
5414
5523
|
}
|
|
5415
5524
|
catch (error) {
|
|
5416
5525
|
const message = error instanceof Error ? error.message : "Failed to open project";
|
|
5417
|
-
this.sessionLogger.error({ err: error, cwd
|
|
5526
|
+
this.sessionLogger.error({ err: error, cwd }, "Failed to open project");
|
|
5418
5527
|
this.emit({
|
|
5419
5528
|
type: "open_project_response",
|
|
5420
5529
|
payload: {
|
|
@@ -7135,7 +7244,6 @@ function isValidGitHubRepoSegment(value) {
|
|
|
7135
7244
|
return /^[A-Za-z0-9._-]+$/.test(value);
|
|
7136
7245
|
}
|
|
7137
7246
|
function toPullRequestTimelinePayloadItem(item) {
|
|
7138
|
-
|
|
7139
|
-
return payload;
|
|
7247
|
+
return item;
|
|
7140
7248
|
}
|
|
7141
7249
|
//# sourceMappingURL=session.js.map
|
|
@@ -85,6 +85,7 @@ function createFallbackWorkspaceGitService() {
|
|
|
85
85
|
unsubscribe: () => { },
|
|
86
86
|
}),
|
|
87
87
|
scheduleRefreshForCwd: () => { },
|
|
88
|
+
onWorkspaceStateMayHaveChanged: () => { },
|
|
88
89
|
dispose: () => { },
|
|
89
90
|
};
|
|
90
91
|
}
|
|
@@ -719,6 +720,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
719
720
|
providersSnapshot: true,
|
|
720
721
|
// COMPAT(checkoutGithubSetAutoMerge): added in v0.1.75, remove gate after 2026-11-13.
|
|
721
722
|
checkoutGithubSetAutoMerge: true,
|
|
723
|
+
githubCheckDetails: true,
|
|
722
724
|
// COMPAT(daemonStatusRpc): added in v0.1.76, remove gate after 2026-11-18.
|
|
723
725
|
daemonStatusRpc: true,
|
|
724
726
|
// COMPAT(terminalRestoreModes): added in v0.1.81, remove gate after 2026-11-23.
|
|
@@ -89,6 +89,7 @@ export interface WorkspaceGitService {
|
|
|
89
89
|
unsubscribe: () => void;
|
|
90
90
|
}>;
|
|
91
91
|
scheduleRefreshForCwd(cwd: string): void;
|
|
92
|
+
onWorkspaceStateMayHaveChanged(cwd: string): void;
|
|
92
93
|
dispose(): void;
|
|
93
94
|
}
|
|
94
95
|
export type WorkspaceGitListener = (snapshot: WorkspaceGitRuntimeSnapshot) => void;
|
|
@@ -201,6 +202,7 @@ export declare class WorkspaceGitServiceImpl implements WorkspaceGitService {
|
|
|
201
202
|
unsubscribe: () => void;
|
|
202
203
|
}>;
|
|
203
204
|
scheduleRefreshForCwd(cwd: string): void;
|
|
205
|
+
onWorkspaceStateMayHaveChanged(cwd: string): void;
|
|
204
206
|
dispose(): void;
|
|
205
207
|
private ensureWorkspaceTarget;
|
|
206
208
|
private readAuxiliaryCache;
|
|
@@ -233,7 +235,8 @@ export declare class WorkspaceGitServiceImpl implements WorkspaceGitService {
|
|
|
233
235
|
private normalizeRefreshRequest;
|
|
234
236
|
private resolveGitHubRemoteForTarget;
|
|
235
237
|
private shouldThrottleNonForcedRefresh;
|
|
236
|
-
private
|
|
238
|
+
private buildScheduledRefreshRequest;
|
|
239
|
+
private mergeRefreshRequests;
|
|
237
240
|
private runWorkspaceRefreshLoop;
|
|
238
241
|
private refreshSnapshot;
|
|
239
242
|
private refreshGitSnapshot;
|