@getpaseo/server 0.1.91-beta.2 → 0.1.92
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/scripts/supervisor.js +21 -0
- package/dist/server/server/agent/agent-manager.d.ts +13 -5
- package/dist/server/server/agent/agent-manager.js +110 -74
- 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/prompt-attachments.js +8 -0
- package/dist/server/server/agent/provider-registry.d.ts +0 -1
- package/dist/server/server/agent/provider-registry.js +55 -16
- 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/codex-app-server-agent.d.ts +3 -2
- package/dist/server/server/agent/providers/codex-app-server-agent.js +29 -26
- 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 +3 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +12 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +18 -3
- package/dist/server/server/agent/providers/opencode-agent.js +135 -36
- package/dist/server/server/agent/providers/pi/agent.d.ts +25 -2
- package/dist/server/server/agent/providers/pi/agent.js +243 -14
- package/dist/server/server/agent/providers/pi/cli-runtime.js +9 -0
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +9 -0
- package/dist/server/server/agent/providers/pi/runtime.d.ts +2 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +11 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.js +284 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +8 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +22 -0
- package/dist/server/server/agent/runtime-mcp-config.d.ts +8 -0
- package/dist/server/server/agent/runtime-mcp-config.js +50 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +2 -2
- package/dist/server/server/daemon-worker.js +84 -1
- package/dist/server/server/file-upload/index.d.ts +27 -0
- package/dist/server/server/file-upload/index.js +158 -0
- package/dist/server/server/loop-service.d.ts +12 -12
- package/dist/server/server/persisted-config.d.ts +8 -0
- package/dist/server/server/persisted-config.js +1 -1
- package/dist/server/server/persistence-hooks.js +6 -4
- package/dist/server/server/session.d.ts +5 -2
- package/dist/server/server/session.js +20 -3
- package/dist/server/server/speech/providers/local/runtime.js +1 -0
- package/dist/server/server/speech/providers/local/worker-client.d.ts +14 -1
- package/dist/server/server/speech/providers/local/worker-client.js +169 -7
- package/dist/server/server/websocket-server.d.ts +2 -0
- package/dist/server/server/websocket-server.js +20 -7
- package/dist/server/server/workspace-registry.d.ts +4 -4
- package/dist/src/server/persisted-config.js +1 -1
- package/package.json +5 -5
|
@@ -15,6 +15,10 @@ export declare class FakePiSession implements PiRuntimeSession {
|
|
|
15
15
|
message: string;
|
|
16
16
|
imageCount: number;
|
|
17
17
|
}>;
|
|
18
|
+
readonly compactRequests: Array<{
|
|
19
|
+
customInstructions?: string;
|
|
20
|
+
}>;
|
|
21
|
+
readonly setAutoCompactionRequests: boolean[];
|
|
18
22
|
readonly setModelRequests: Array<{
|
|
19
23
|
provider: string;
|
|
20
24
|
modelId: string;
|
|
@@ -41,6 +45,8 @@ export declare class FakePiSession implements PiRuntimeSession {
|
|
|
41
45
|
messages: PiAgentMessage[];
|
|
42
46
|
stats: PiSessionStats;
|
|
43
47
|
commands: PiRpcSlashCommand[];
|
|
48
|
+
compactError: Error | null;
|
|
49
|
+
emitCompactEnd: boolean;
|
|
44
50
|
state: PiSessionState;
|
|
45
51
|
private readonly subscribers;
|
|
46
52
|
constructor(launch: PiRuntimeLaunch);
|
|
@@ -50,6 +56,8 @@ export declare class FakePiSession implements PiRuntimeSession {
|
|
|
50
56
|
data: string;
|
|
51
57
|
mimeType: string;
|
|
52
58
|
}>): Promise<void>;
|
|
59
|
+
compact(customInstructions?: string): Promise<void>;
|
|
60
|
+
setAutoCompaction(enabled: boolean): Promise<void>;
|
|
53
61
|
abort(): Promise<void>;
|
|
54
62
|
getState(): Promise<PiSessionState>;
|
|
55
63
|
getMessages(): Promise<PiAgentMessage[]>;
|
|
@@ -31,6 +31,8 @@ export class FakePi {
|
|
|
31
31
|
export class FakePiSession {
|
|
32
32
|
constructor(launch) {
|
|
33
33
|
this.prompts = [];
|
|
34
|
+
this.compactRequests = [];
|
|
35
|
+
this.setAutoCompactionRequests = [];
|
|
34
36
|
this.setModelRequests = [];
|
|
35
37
|
this.setThinkingLevelRequests = [];
|
|
36
38
|
this.treeNavigationRequests = [];
|
|
@@ -46,12 +48,15 @@ export class FakePiSession {
|
|
|
46
48
|
cost: 0,
|
|
47
49
|
};
|
|
48
50
|
this.commands = [];
|
|
51
|
+
this.compactError = null;
|
|
52
|
+
this.emitCompactEnd = true;
|
|
49
53
|
this.subscribers = new Set();
|
|
50
54
|
this.state = {
|
|
51
55
|
model: null,
|
|
52
56
|
thinkingLevel: "medium",
|
|
53
57
|
isStreaming: false,
|
|
54
58
|
isCompacting: false,
|
|
59
|
+
autoCompactionEnabled: true,
|
|
55
60
|
sessionFile: launch.session ?? "/tmp/pi-session",
|
|
56
61
|
sessionId: "pi-session-1",
|
|
57
62
|
messageCount: 0,
|
|
@@ -69,6 +74,23 @@ export class FakePiSession {
|
|
|
69
74
|
this.handleTreeNavigationCommand(message);
|
|
70
75
|
this.handleEntryCaptureCommand(message);
|
|
71
76
|
}
|
|
77
|
+
async compact(customInstructions) {
|
|
78
|
+
this.compactRequests.push(customInstructions === undefined ? {} : { customInstructions });
|
|
79
|
+
this.emit({ type: "compaction_start", reason: "manual" });
|
|
80
|
+
if (this.emitCompactEnd) {
|
|
81
|
+
this.emit({ type: "compaction_end", reason: "manual" });
|
|
82
|
+
}
|
|
83
|
+
if (this.compactError) {
|
|
84
|
+
throw this.compactError;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async setAutoCompaction(enabled) {
|
|
88
|
+
this.setAutoCompactionRequests.push(enabled);
|
|
89
|
+
this.state = {
|
|
90
|
+
...this.state,
|
|
91
|
+
autoCompactionEnabled: enabled,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
72
94
|
async abort() {
|
|
73
95
|
this.abortRequested = true;
|
|
74
96
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentSessionConfig } from "./agent-sdk-types.js";
|
|
2
|
+
export declare function stripInternalPaseoMcpServer(config: AgentSessionConfig): AgentSessionConfig;
|
|
3
|
+
export declare function withRuntimePaseoMcpServer(params: {
|
|
4
|
+
config: AgentSessionConfig;
|
|
5
|
+
agentId: string;
|
|
6
|
+
mcpBaseUrl: string | null;
|
|
7
|
+
}): AgentSessionConfig;
|
|
8
|
+
//# sourceMappingURL=runtime-mcp-config.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const PASEO_MCP_SERVER_NAME = "paseo";
|
|
2
|
+
const PASEO_MCP_PATHNAME = "/mcp/agents";
|
|
3
|
+
export function stripInternalPaseoMcpServer(config) {
|
|
4
|
+
const mcpServers = config.mcpServers;
|
|
5
|
+
if (!mcpServers) {
|
|
6
|
+
return config;
|
|
7
|
+
}
|
|
8
|
+
const paseoServer = mcpServers[PASEO_MCP_SERVER_NAME];
|
|
9
|
+
if (!paseoServer || !isInternalPaseoMcpServer(paseoServer)) {
|
|
10
|
+
return config;
|
|
11
|
+
}
|
|
12
|
+
const nextMcpServers = { ...mcpServers };
|
|
13
|
+
delete nextMcpServers[PASEO_MCP_SERVER_NAME];
|
|
14
|
+
const next = { ...config };
|
|
15
|
+
if (Object.keys(nextMcpServers).length > 0) {
|
|
16
|
+
next.mcpServers = nextMcpServers;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
delete next.mcpServers;
|
|
20
|
+
}
|
|
21
|
+
return next;
|
|
22
|
+
}
|
|
23
|
+
export function withRuntimePaseoMcpServer(params) {
|
|
24
|
+
const storedConfig = stripInternalPaseoMcpServer(params.config);
|
|
25
|
+
if (!params.mcpBaseUrl || storedConfig.mcpServers?.[PASEO_MCP_SERVER_NAME]) {
|
|
26
|
+
return storedConfig;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
...storedConfig,
|
|
30
|
+
mcpServers: {
|
|
31
|
+
[PASEO_MCP_SERVER_NAME]: {
|
|
32
|
+
type: "http",
|
|
33
|
+
url: `${params.mcpBaseUrl}?callerAgentId=${params.agentId}`,
|
|
34
|
+
},
|
|
35
|
+
...storedConfig.mcpServers,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function isInternalPaseoMcpServer(config) {
|
|
40
|
+
if (config.type !== "http" && config.type !== "sse") {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return new URL(config.url).pathname === PASEO_MCP_PATHNAME;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=runtime-mcp-config.js.map
|
|
@@ -34,10 +34,10 @@ export async function archiveIfSafe(input) {
|
|
|
34
34
|
if (!snapshot) {
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
|
-
if (snapshot.git.isDirty === true
|
|
37
|
+
if (snapshot.git.isDirty === true) {
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
if (snapshot.git.aheadOfOrigin > 0) {
|
|
40
|
+
if (typeof snapshot.git.aheadOfOrigin === "number" && snapshot.git.aheadOfOrigin > 0) {
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
const ownership = await deps.isPaseoOwnedWorktreeCwd(cwd, {
|
|
@@ -1,8 +1,39 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { createPaseoDaemon } from "./bootstrap.js";
|
|
2
4
|
import { loadConfig } from "./config.js";
|
|
3
5
|
import { resolvePaseoHome } from "./paseo-home.js";
|
|
4
6
|
import { createRootLogger } from "./logger.js";
|
|
5
7
|
process.title = "Paseo Daemon";
|
|
8
|
+
function isPidAlive(pid) {
|
|
9
|
+
try {
|
|
10
|
+
process.kill(pid, 0);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "EPERM") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function writeWorkerLifecycleLog(paseoHome, message, fields = {}) {
|
|
21
|
+
try {
|
|
22
|
+
const logPath = path.join(paseoHome, "daemon.log");
|
|
23
|
+
mkdirSync(path.dirname(logPath), { recursive: true });
|
|
24
|
+
appendFileSync(logPath, `${JSON.stringify({
|
|
25
|
+
level: "warn",
|
|
26
|
+
time: new Date().toISOString(),
|
|
27
|
+
pid: process.pid,
|
|
28
|
+
name: "DaemonWorker",
|
|
29
|
+
msg: message,
|
|
30
|
+
...fields,
|
|
31
|
+
})}\n`, "utf8");
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Exit-reason logging must never prevent the worker from exiting.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
6
37
|
function bootstrapFromEnvironment() {
|
|
7
38
|
try {
|
|
8
39
|
const paseoHome = resolvePaseoHome();
|
|
@@ -31,7 +62,7 @@ function applyCliFlagOverrides(config) {
|
|
|
31
62
|
}
|
|
32
63
|
}
|
|
33
64
|
async function main() {
|
|
34
|
-
const { logger, config } = bootstrapFromEnvironment();
|
|
65
|
+
const { paseoHome, logger, config } = bootstrapFromEnvironment();
|
|
35
66
|
let daemon = null;
|
|
36
67
|
let shutdownPromise = null;
|
|
37
68
|
let exitHookInstalled = false;
|
|
@@ -107,6 +138,58 @@ async function main() {
|
|
|
107
138
|
}
|
|
108
139
|
beginShutdown("restart lifecycle intent", { successExitCode: 0 });
|
|
109
140
|
};
|
|
141
|
+
const installSupervisorLivenessGuard = () => {
|
|
142
|
+
if (typeof process.send !== "function") {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const supervisorPid = process.ppid;
|
|
146
|
+
let lastSupervisorHeartbeatAt = Date.now();
|
|
147
|
+
let supervisorExitRequested = false;
|
|
148
|
+
const exitAfterSupervisorLoss = (reason) => {
|
|
149
|
+
if (supervisorExitRequested) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
supervisorExitRequested = true;
|
|
153
|
+
writeWorkerLifecycleLog(paseoHome, "Supervisor liveness lost; worker exiting", {
|
|
154
|
+
reason,
|
|
155
|
+
supervisorPid,
|
|
156
|
+
currentParentPid: process.ppid,
|
|
157
|
+
ipcConnected: typeof process.connected === "boolean" ? process.connected : null,
|
|
158
|
+
heartbeatAgeMs: Date.now() - lastSupervisorHeartbeatAt,
|
|
159
|
+
});
|
|
160
|
+
// The supervisor owns the worker's stdout/stderr pipes. Once it is gone,
|
|
161
|
+
// logging during graceful shutdown can block on the broken pipe and leave
|
|
162
|
+
// the daemon orphaned, so supervisor loss is a hard process boundary.
|
|
163
|
+
process.exit(0);
|
|
164
|
+
};
|
|
165
|
+
process.on("message", (message) => {
|
|
166
|
+
if (typeof message === "object" &&
|
|
167
|
+
message !== null &&
|
|
168
|
+
"type" in message &&
|
|
169
|
+
message.type === "paseo:supervisor-heartbeat") {
|
|
170
|
+
lastSupervisorHeartbeatAt = Date.now();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
process.on("disconnect", () => exitAfterSupervisorLoss("ipc_disconnect_event"));
|
|
174
|
+
const timer = setInterval(() => {
|
|
175
|
+
const ipcConnected = typeof process.connected === "boolean" ? process.connected : true;
|
|
176
|
+
const heartbeatExpired = Date.now() - lastSupervisorHeartbeatAt > 3500;
|
|
177
|
+
const supervisorChanged = process.ppid !== supervisorPid;
|
|
178
|
+
if (ipcConnected === false) {
|
|
179
|
+
exitAfterSupervisorLoss("ipc_disconnected");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (supervisorChanged) {
|
|
183
|
+
exitAfterSupervisorLoss("supervisor_parent_pid_changed");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (heartbeatExpired && !isPidAlive(supervisorPid)) {
|
|
187
|
+
exitAfterSupervisorLoss("supervisor_pid_dead");
|
|
188
|
+
}
|
|
189
|
+
}, 1000);
|
|
190
|
+
timer.unref();
|
|
191
|
+
};
|
|
192
|
+
installSupervisorLivenessGuard();
|
|
110
193
|
try {
|
|
111
194
|
daemon = await createPaseoDaemon({
|
|
112
195
|
...config,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type FileTransferFrame } from "@getpaseo/protocol/binary-frames/index";
|
|
2
|
+
import type { FileUploadRequest, FileUploadResponse } from "../messages.js";
|
|
3
|
+
interface FileUploadStoreOptions {
|
|
4
|
+
paseoHome: string;
|
|
5
|
+
staleUploadTimeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class FileUploadStore {
|
|
8
|
+
private static readonly defaultStaleUploadTimeoutMs;
|
|
9
|
+
private readonly paseoHome;
|
|
10
|
+
private readonly staleUploadTimeoutMs;
|
|
11
|
+
private readonly pending;
|
|
12
|
+
constructor(options: FileUploadStoreOptions);
|
|
13
|
+
beginUpload(request: FileUploadRequest): void;
|
|
14
|
+
receiveFrame(frame: FileTransferFrame): Promise<FileUploadResponse | null>;
|
|
15
|
+
private applyFrame;
|
|
16
|
+
private startWriting;
|
|
17
|
+
private writeChunk;
|
|
18
|
+
private completeUpload;
|
|
19
|
+
private createStaleUploadTimeout;
|
|
20
|
+
private refreshStaleUploadTimeout;
|
|
21
|
+
private expireStaleUpload;
|
|
22
|
+
private clearPendingUpload;
|
|
23
|
+
private removeFailedUpload;
|
|
24
|
+
private removeUploadDirectory;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { appendFile, mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { FileTransferOpcode } from "@getpaseo/protocol/binary-frames/index";
|
|
4
|
+
import { getErrorMessage } from "@getpaseo/protocol/error-utils";
|
|
5
|
+
export class FileUploadStore {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.pending = new Map();
|
|
8
|
+
this.paseoHome = options.paseoHome;
|
|
9
|
+
this.staleUploadTimeoutMs =
|
|
10
|
+
options.staleUploadTimeoutMs ?? FileUploadStore.defaultStaleUploadTimeoutMs;
|
|
11
|
+
}
|
|
12
|
+
beginUpload(request) {
|
|
13
|
+
const existingUpload = this.pending.get(request.requestId);
|
|
14
|
+
if (existingUpload) {
|
|
15
|
+
this.clearPendingUpload(existingUpload);
|
|
16
|
+
void existingUpload.queue.then(() => this.removeUploadDirectory(existingUpload));
|
|
17
|
+
}
|
|
18
|
+
const fileName = sanitizeFileName(request.fileName);
|
|
19
|
+
const attempt = existingUpload ? existingUpload.attempt + 1 : 1;
|
|
20
|
+
const id = buildUploadId(request.requestId, attempt);
|
|
21
|
+
const uploadDir = join(this.paseoHome, "uploads", id);
|
|
22
|
+
const upload = {
|
|
23
|
+
requestId: request.requestId,
|
|
24
|
+
id,
|
|
25
|
+
attempt,
|
|
26
|
+
fileName,
|
|
27
|
+
mimeType: request.mimeType,
|
|
28
|
+
size: request.size,
|
|
29
|
+
path: join(uploadDir, fileName),
|
|
30
|
+
receivedBytes: 0,
|
|
31
|
+
started: false,
|
|
32
|
+
staleTimeout: this.createStaleUploadTimeout(request.requestId),
|
|
33
|
+
queue: Promise.resolve(),
|
|
34
|
+
};
|
|
35
|
+
this.pending.set(request.requestId, upload);
|
|
36
|
+
}
|
|
37
|
+
async receiveFrame(frame) {
|
|
38
|
+
const upload = this.pending.get(frame.requestId);
|
|
39
|
+
if (!upload) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
this.refreshStaleUploadTimeout(upload);
|
|
43
|
+
const operation = upload.queue.then(() => this.applyFrame(upload, frame));
|
|
44
|
+
upload.queue = operation.then(() => undefined, () => undefined);
|
|
45
|
+
return operation;
|
|
46
|
+
}
|
|
47
|
+
async applyFrame(upload, frame) {
|
|
48
|
+
if (this.pending.get(upload.requestId) !== upload) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
if (frame.opcode === FileTransferOpcode.FileBegin) {
|
|
53
|
+
await this.startWriting(upload);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (frame.opcode === FileTransferOpcode.FileChunk) {
|
|
57
|
+
await this.writeChunk(upload, frame.payload);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return await this.completeUpload(upload);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
await this.removeFailedUpload(upload);
|
|
64
|
+
return buildUploadResponse(upload, getErrorMessage(error));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async startWriting(upload) {
|
|
68
|
+
await mkdir(join(this.paseoHome, "uploads", upload.id), { recursive: true });
|
|
69
|
+
await writeFile(upload.path, new Uint8Array());
|
|
70
|
+
upload.started = true;
|
|
71
|
+
}
|
|
72
|
+
async writeChunk(upload, bytes) {
|
|
73
|
+
if (!upload.started) {
|
|
74
|
+
throw new Error("Upload chunks arrived before file begin.");
|
|
75
|
+
}
|
|
76
|
+
const nextReceivedBytes = upload.receivedBytes + bytes.byteLength;
|
|
77
|
+
if (nextReceivedBytes > upload.size) {
|
|
78
|
+
throw new Error(`Upload exceeded declared size: expected ${upload.size}, received ${nextReceivedBytes}.`);
|
|
79
|
+
}
|
|
80
|
+
await appendFile(upload.path, bytes);
|
|
81
|
+
upload.receivedBytes += bytes.byteLength;
|
|
82
|
+
}
|
|
83
|
+
async completeUpload(upload) {
|
|
84
|
+
this.clearPendingUpload(upload);
|
|
85
|
+
if (upload.receivedBytes !== upload.size) {
|
|
86
|
+
await this.removeUploadDirectory(upload);
|
|
87
|
+
return buildUploadResponse(upload, `Upload size mismatch: expected ${upload.size}, received ${upload.receivedBytes}.`);
|
|
88
|
+
}
|
|
89
|
+
return buildUploadResponse(upload, null);
|
|
90
|
+
}
|
|
91
|
+
createStaleUploadTimeout(requestId) {
|
|
92
|
+
const timeout = setTimeout(() => {
|
|
93
|
+
this.expireStaleUpload(requestId);
|
|
94
|
+
}, this.staleUploadTimeoutMs);
|
|
95
|
+
timeout.unref?.();
|
|
96
|
+
return timeout;
|
|
97
|
+
}
|
|
98
|
+
refreshStaleUploadTimeout(upload) {
|
|
99
|
+
clearTimeout(upload.staleTimeout);
|
|
100
|
+
upload.staleTimeout = this.createStaleUploadTimeout(upload.requestId);
|
|
101
|
+
}
|
|
102
|
+
expireStaleUpload(requestId) {
|
|
103
|
+
const upload = this.pending.get(requestId);
|
|
104
|
+
if (!upload) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.clearPendingUpload(upload);
|
|
108
|
+
const cleanup = upload.queue.then(() => this.removeUploadDirectory(upload), () => this.removeUploadDirectory(upload));
|
|
109
|
+
upload.queue = cleanup.then(() => undefined, () => undefined);
|
|
110
|
+
}
|
|
111
|
+
clearPendingUpload(upload) {
|
|
112
|
+
clearTimeout(upload.staleTimeout);
|
|
113
|
+
if (this.pending.get(upload.requestId) === upload) {
|
|
114
|
+
this.pending.delete(upload.requestId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async removeFailedUpload(upload) {
|
|
118
|
+
this.clearPendingUpload(upload);
|
|
119
|
+
await this.removeUploadDirectory(upload);
|
|
120
|
+
}
|
|
121
|
+
async removeUploadDirectory(upload) {
|
|
122
|
+
await rm(join(this.paseoHome, "uploads", upload.id), { recursive: true, force: true }).catch(() => undefined);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
FileUploadStore.defaultStaleUploadTimeoutMs = 10 * 60 * 1000;
|
|
126
|
+
function buildUploadResponse(upload, error) {
|
|
127
|
+
return {
|
|
128
|
+
type: "file.upload.response",
|
|
129
|
+
payload: {
|
|
130
|
+
requestId: upload.requestId,
|
|
131
|
+
file: error
|
|
132
|
+
? null
|
|
133
|
+
: {
|
|
134
|
+
type: "uploaded_file",
|
|
135
|
+
id: upload.id,
|
|
136
|
+
fileName: upload.fileName,
|
|
137
|
+
mimeType: upload.mimeType,
|
|
138
|
+
size: upload.size,
|
|
139
|
+
path: upload.path,
|
|
140
|
+
},
|
|
141
|
+
error,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function sanitizeUploadId(value) {
|
|
146
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_") || "file";
|
|
147
|
+
}
|
|
148
|
+
function buildUploadId(requestId, attempt) {
|
|
149
|
+
const baseId = `upload_${sanitizeUploadId(requestId)}`;
|
|
150
|
+
return attempt === 1 ? baseId : `${baseId}_${attempt}`;
|
|
151
|
+
}
|
|
152
|
+
function sanitizeFileName(value) {
|
|
153
|
+
const name = basename(value)
|
|
154
|
+
.replace(/[^a-zA-Z0-9._ -]/g, "_")
|
|
155
|
+
.trim();
|
|
156
|
+
return name.length > 0 && name !== "." && name !== ".." ? name : "upload";
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -13,16 +13,16 @@ declare const LoopLogEntrySchema: z.ZodObject<{
|
|
|
13
13
|
}, "strip", z.ZodTypeAny, {
|
|
14
14
|
text: string;
|
|
15
15
|
level: "error" | "info";
|
|
16
|
-
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
17
|
-
timestamp: string;
|
|
18
16
|
seq: number;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
19
19
|
iteration: number | null;
|
|
20
20
|
}, {
|
|
21
21
|
text: string;
|
|
22
22
|
level: "error" | "info";
|
|
23
|
-
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
24
|
-
timestamp: string;
|
|
25
23
|
seq: number;
|
|
24
|
+
timestamp: string;
|
|
25
|
+
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
26
26
|
iteration: number | null;
|
|
27
27
|
}>;
|
|
28
28
|
declare const LoopVerifyCheckResultSchema: z.ZodObject<{
|
|
@@ -312,16 +312,16 @@ declare const LoopRecordSchema: z.ZodObject<{
|
|
|
312
312
|
}, "strip", z.ZodTypeAny, {
|
|
313
313
|
text: string;
|
|
314
314
|
level: "error" | "info";
|
|
315
|
-
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
316
|
-
timestamp: string;
|
|
317
315
|
seq: number;
|
|
316
|
+
timestamp: string;
|
|
317
|
+
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
318
318
|
iteration: number | null;
|
|
319
319
|
}, {
|
|
320
320
|
text: string;
|
|
321
321
|
level: "error" | "info";
|
|
322
|
-
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
323
|
-
timestamp: string;
|
|
324
322
|
seq: number;
|
|
323
|
+
timestamp: string;
|
|
324
|
+
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
325
325
|
iteration: number | null;
|
|
326
326
|
}>, "many">;
|
|
327
327
|
nextLogSeq: z.ZodNumber;
|
|
@@ -382,9 +382,9 @@ declare const LoopRecordSchema: z.ZodObject<{
|
|
|
382
382
|
logs: {
|
|
383
383
|
text: string;
|
|
384
384
|
level: "error" | "info";
|
|
385
|
-
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
386
|
-
timestamp: string;
|
|
387
385
|
seq: number;
|
|
386
|
+
timestamp: string;
|
|
387
|
+
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
388
388
|
iteration: number | null;
|
|
389
389
|
}[];
|
|
390
390
|
nextLogSeq: number;
|
|
@@ -443,9 +443,9 @@ declare const LoopRecordSchema: z.ZodObject<{
|
|
|
443
443
|
logs: {
|
|
444
444
|
text: string;
|
|
445
445
|
level: "error" | "info";
|
|
446
|
-
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
447
|
-
timestamp: string;
|
|
448
446
|
seq: number;
|
|
447
|
+
timestamp: string;
|
|
448
|
+
source: "worker" | "loop" | "verifier" | "verify-check";
|
|
449
449
|
iteration: number | null;
|
|
450
450
|
}[];
|
|
451
451
|
nextLogSeq: number;
|
|
@@ -230,6 +230,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
230
230
|
description: z.ZodOptional<z.ZodString>;
|
|
231
231
|
command: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
232
232
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
233
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
233
234
|
models: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
234
235
|
id: z.ZodString;
|
|
235
236
|
label: z.ZodString;
|
|
@@ -323,6 +324,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
323
324
|
order: z.ZodOptional<z.ZodNumber>;
|
|
324
325
|
}, "strip", z.ZodTypeAny, {
|
|
325
326
|
description?: string | undefined;
|
|
327
|
+
params?: Record<string, unknown> | undefined;
|
|
326
328
|
label?: string | undefined;
|
|
327
329
|
enabled?: boolean | undefined;
|
|
328
330
|
command?: string[] | undefined;
|
|
@@ -356,6 +358,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
356
358
|
order?: number | undefined;
|
|
357
359
|
}, {
|
|
358
360
|
description?: string | undefined;
|
|
361
|
+
params?: Record<string, unknown> | undefined;
|
|
359
362
|
label?: string | undefined;
|
|
360
363
|
enabled?: boolean | undefined;
|
|
361
364
|
command?: string[] | undefined;
|
|
@@ -389,6 +392,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
389
392
|
order?: number | undefined;
|
|
390
393
|
}>>, Record<string, {
|
|
391
394
|
description?: string | undefined;
|
|
395
|
+
params?: Record<string, unknown> | undefined;
|
|
392
396
|
label?: string | undefined;
|
|
393
397
|
enabled?: boolean | undefined;
|
|
394
398
|
command?: string[] | undefined;
|
|
@@ -422,6 +426,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
422
426
|
order?: number | undefined;
|
|
423
427
|
}>, Record<string, {
|
|
424
428
|
description?: string | undefined;
|
|
429
|
+
params?: Record<string, unknown> | undefined;
|
|
425
430
|
label?: string | undefined;
|
|
426
431
|
enabled?: boolean | undefined;
|
|
427
432
|
command?: string[] | undefined;
|
|
@@ -455,6 +460,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
455
460
|
order?: number | undefined;
|
|
456
461
|
}>>, Record<string, {
|
|
457
462
|
description?: string | undefined;
|
|
463
|
+
params?: Record<string, unknown> | undefined;
|
|
458
464
|
label?: string | undefined;
|
|
459
465
|
enabled?: boolean | undefined;
|
|
460
466
|
command?: string[] | undefined;
|
|
@@ -524,6 +530,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
524
530
|
} | undefined;
|
|
525
531
|
providers?: Record<string, {
|
|
526
532
|
description?: string | undefined;
|
|
533
|
+
params?: Record<string, unknown> | undefined;
|
|
527
534
|
label?: string | undefined;
|
|
528
535
|
enabled?: boolean | undefined;
|
|
529
536
|
command?: string[] | undefined;
|
|
@@ -925,6 +932,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
|
|
|
925
932
|
} | undefined;
|
|
926
933
|
providers?: Record<string, {
|
|
927
934
|
description?: string | undefined;
|
|
935
|
+
params?: Record<string, unknown> | undefined;
|
|
928
936
|
label?: string | undefined;
|
|
929
937
|
enabled?: boolean | undefined;
|
|
930
938
|
command?: string[] | undefined;
|
|
@@ -129,7 +129,7 @@ const AgentMetadataGenerationSchema = z
|
|
|
129
129
|
providers: z.array(StructuredGenerationProviderConfigSchema).optional(),
|
|
130
130
|
})
|
|
131
131
|
.strict();
|
|
132
|
-
const BUILTIN_PROVIDER_IDS = ["claude", "codex", "copilot", "opencode", "pi"];
|
|
132
|
+
const BUILTIN_PROVIDER_IDS = ["claude", "codex", "copilot", "opencode", "pi", "omp"];
|
|
133
133
|
function isLegacyProviderEntry(value) {
|
|
134
134
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
135
135
|
return false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripInternalPaseoMcpServer } from "./agent/runtime-mcp-config.js";
|
|
1
2
|
function getLogger(logger) {
|
|
2
3
|
return logger.child({ module: "persistence" });
|
|
3
4
|
}
|
|
@@ -21,7 +22,8 @@ export function attachAgentStoragePersistence(logger, agentManager, storage) {
|
|
|
21
22
|
return unsubscribe;
|
|
22
23
|
}
|
|
23
24
|
export function buildConfigOverrides(record) {
|
|
24
|
-
return {
|
|
25
|
+
return stripInternalPaseoMcpServer({
|
|
26
|
+
provider: record.provider,
|
|
25
27
|
cwd: record.cwd,
|
|
26
28
|
modeId: record.lastModeId ?? record.config?.modeId ?? undefined,
|
|
27
29
|
model: record.config?.model ?? undefined,
|
|
@@ -30,7 +32,7 @@ export function buildConfigOverrides(record) {
|
|
|
30
32
|
extra: record.config?.extra ?? undefined,
|
|
31
33
|
systemPrompt: record.config?.systemPrompt ?? undefined,
|
|
32
34
|
mcpServers: record.config?.mcpServers ?? undefined,
|
|
33
|
-
};
|
|
35
|
+
});
|
|
34
36
|
}
|
|
35
37
|
export function buildSessionConfig(record, options) {
|
|
36
38
|
const validProviders = options?.validProviders;
|
|
@@ -39,7 +41,7 @@ export function buildSessionConfig(record, options) {
|
|
|
39
41
|
return null;
|
|
40
42
|
}
|
|
41
43
|
const overrides = buildConfigOverrides(record);
|
|
42
|
-
return {
|
|
44
|
+
return stripInternalPaseoMcpServer({
|
|
43
45
|
provider: record.provider,
|
|
44
46
|
cwd: record.cwd,
|
|
45
47
|
modeId: overrides.modeId,
|
|
@@ -49,7 +51,7 @@ export function buildSessionConfig(record, options) {
|
|
|
49
51
|
extra: overrides.extra,
|
|
50
52
|
systemPrompt: overrides.systemPrompt,
|
|
51
53
|
mcpServers: overrides.mcpServers,
|
|
52
|
-
};
|
|
54
|
+
});
|
|
53
55
|
}
|
|
54
56
|
export function isStoredAgentProviderAvailable(record, validProviders) {
|
|
55
57
|
return buildSessionConfig(record, { validProviders }) !== null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ClientCapability } from "@getpaseo/protocol/client-capabilities";
|
|
2
2
|
import { type AgentSnapshotPayload, type SessionInboundMessage, type SessionOutboundMessage, type WorkspaceSetupSnapshot } from "./messages.js";
|
|
3
3
|
import type { TerminalManager } from "../terminal/terminal-manager.js";
|
|
4
|
-
import { type
|
|
4
|
+
import { type BinaryFrame } from "@getpaseo/protocol/binary-frames/index";
|
|
5
5
|
import type { SpeechToTextProvider, TextToSpeechProvider } from "./speech/speech-provider.js";
|
|
6
6
|
import type { TurnDetectionProvider } from "./speech/turn-detection-provider.js";
|
|
7
7
|
import type { VoiceCallerContext, VoiceSpeakHandler } from "./voice-types.js";
|
|
@@ -181,6 +181,7 @@ export declare class Session {
|
|
|
181
181
|
private readonly workspaceSetupSnapshots;
|
|
182
182
|
private readonly workspaceGitFetchSubscriptions;
|
|
183
183
|
private readonly workspaceGitSubscriptions;
|
|
184
|
+
private readonly fileUploads;
|
|
184
185
|
private readonly workspaceDirectory;
|
|
185
186
|
private registerVoiceSpeakHandler?;
|
|
186
187
|
private unregisterVoiceSpeakHandler?;
|
|
@@ -286,7 +287,7 @@ export declare class Session {
|
|
|
286
287
|
private dispatchScheduleMessage;
|
|
287
288
|
private dispatchMiscMessage;
|
|
288
289
|
resetPeakInflight(): void;
|
|
289
|
-
handleBinaryFrame(
|
|
290
|
+
handleBinaryFrame(binaryFrame: BinaryFrame): Promise<void>;
|
|
290
291
|
private handleRestartServerRequest;
|
|
291
292
|
private handleShutdownServerRequest;
|
|
292
293
|
private emitLifecycleIntent;
|
|
@@ -420,6 +421,8 @@ export declare class Session {
|
|
|
420
421
|
* Handle read-only file explorer requests scoped to a workspace cwd
|
|
421
422
|
*/
|
|
422
423
|
private handleFileExplorerRequest;
|
|
424
|
+
private handleFileUploadRequest;
|
|
425
|
+
private handleFileTransferFrame;
|
|
423
426
|
/**
|
|
424
427
|
* Handle project icon request for a given cwd
|
|
425
428
|
*/
|