@getpaseo/server 0.1.89 → 0.1.91-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/server/agent/agent-prompt.js +4 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +1 -0
- package/dist/server/server/agent/agent-storage.js +2 -9
- package/dist/server/server/agent/create-agent/create.js +10 -2
- package/dist/server/server/agent/create-agent-mode.d.ts +3 -8
- package/dist/server/server/agent/create-agent-mode.js +16 -2
- package/dist/server/server/agent/import-sessions.js +1 -1
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +2 -1
- package/dist/server/server/agent/provider-snapshot-manager.js +18 -2
- package/dist/server/server/agent/providers/acp-agent.d.ts +3 -3
- package/dist/server/server/agent/providers/acp-agent.js +18 -13
- package/dist/server/server/agent/providers/claude/agent.js +3 -7
- package/dist/server/server/agent/providers/claude/project-dir.d.ts +1 -0
- package/dist/server/server/agent/providers/claude/project-dir.js +14 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js +16 -22
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +2 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +69 -2
- package/dist/server/server/agent/providers/opencode-agent.js +19 -8
- package/dist/server/server/agent/timeline-projection.js +30 -1
- package/dist/server/server/atomic-file.d.ts +3 -0
- package/dist/server/server/atomic-file.js +19 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +4 -1
- package/dist/server/server/bootstrap.js +2 -0
- package/dist/server/server/chat/chat-service.js +2 -4
- package/dist/server/server/daemon-keypair.js +2 -2
- package/dist/server/server/loop-service.d.ts +4 -0
- package/dist/server/server/loop-service.js +27 -9
- package/dist/server/server/persisted-config.js +3 -3
- package/dist/server/server/private-files.d.ts +0 -1
- package/dist/server/server/private-files.js +0 -5
- package/dist/server/server/schedule/service.d.ts +6 -0
- package/dist/server/server/schedule/service.js +41 -18
- package/dist/server/server/schedule/store.js +3 -2
- package/dist/server/server/server-id.js +3 -3
- package/dist/server/server/session.d.ts +5 -15
- package/dist/server/server/session.js +184 -107
- package/dist/server/server/speech/providers/local/worker-client.js +1 -11
- package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
- package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
- package/dist/server/server/workspace-directory.d.ts +8 -0
- package/dist/server/server/workspace-directory.js +141 -15
- package/dist/server/server/workspace-registry.js +2 -6
- package/dist/server/utils/checkout-git.d.ts +0 -1
- package/dist/server/utils/checkout-git.js +23 -31
- package/dist/src/server/persisted-config.js +3 -3
- package/dist/src/server/private-files.js +0 -5
- package/package.json +9 -7
- package/dist/server/server/editor-targets.d.ts +0 -18
- package/dist/server/server/editor-targets.js +0 -109
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { createPathEquivalenceMatcher } from "../../../utils/path.js";
|
|
3
|
+
import pLimit from "p-limit";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
|
|
5
6
|
import { isDefaultAgentCreateConfigUnattended, resolveDefaultAgentCreateConfig, } from "../create-agent-mode.js";
|
|
@@ -57,10 +58,17 @@ function withOpenCodeAutoAcceptFeature(featureValues, enabled) {
|
|
|
57
58
|
}
|
|
58
59
|
function resolveOpenCodeCreateConfig(input) {
|
|
59
60
|
const legacyFullAccess = input.requestedMode === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID;
|
|
60
|
-
const
|
|
61
|
-
const
|
|
61
|
+
const parent = input.parent;
|
|
62
|
+
const isUnattendedCreate = input.unattended || parent?.isUnattended === true;
|
|
63
|
+
const inheritsUnattended = input.requestedMode === undefined && isUnattendedCreate;
|
|
64
|
+
const inheritedOpenCodeMode = inheritsUnattended && parent?.provider === input.provider
|
|
65
|
+
? (parent.modeId ?? undefined)
|
|
66
|
+
: undefined;
|
|
67
|
+
const requestedMode = legacyFullAccess
|
|
68
|
+
? OPENCODE_BUILD_MODE_ID
|
|
69
|
+
: (input.requestedMode ?? inheritedOpenCodeMode);
|
|
62
70
|
const featureValues = legacyFullAccess ||
|
|
63
|
-
(
|
|
71
|
+
(isUnattendedCreate && input.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === undefined)
|
|
64
72
|
? withOpenCodeAutoAcceptFeature(input.featureValues, true)
|
|
65
73
|
: input.featureValues;
|
|
66
74
|
if (inheritsUnattended && requestedMode === undefined) {
|
|
@@ -124,6 +132,8 @@ function resolveOpenCodePermissionReply(response) {
|
|
|
124
132
|
}
|
|
125
133
|
const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
|
|
126
134
|
const OPENCODE_PROVIDER_LIST_TIMEOUT_MS = 30000;
|
|
135
|
+
const OPENCODE_METADATA_CONCURRENCY = 4;
|
|
136
|
+
const openCodeMetadataLimit = pLimit(OPENCODE_METADATA_CONCURRENCY);
|
|
127
137
|
const OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS = [
|
|
128
138
|
{ name: "compact", description: "Compact the current session", argumentHint: "" },
|
|
129
139
|
{ name: "summarize", description: "Compact the current session", argumentHint: "" },
|
|
@@ -918,7 +928,7 @@ export class OpenCodeAgentClient {
|
|
|
918
928
|
try {
|
|
919
929
|
// Background model discovery can be legitimately slow while OpenCode refreshes
|
|
920
930
|
// provider state, so allow longer than turn execution paths.
|
|
921
|
-
const response = await withTimeout(client.provider.list({ directory: options.cwd }), OPENCODE_PROVIDER_LIST_TIMEOUT_MS, `OpenCode provider.list timed out after ${OPENCODE_PROVIDER_LIST_TIMEOUT_MS / 1000}s - server may not be authenticated or connected to any providers`);
|
|
931
|
+
const response = await openCodeMetadataLimit(() => withTimeout(client.provider.list({ directory: options.cwd }), OPENCODE_PROVIDER_LIST_TIMEOUT_MS, `OpenCode provider.list timed out after ${OPENCODE_PROVIDER_LIST_TIMEOUT_MS / 1000}s - server may not be authenticated or connected to any providers`));
|
|
922
932
|
if (response.error) {
|
|
923
933
|
throw new Error(`Failed to fetch OpenCode providers: ${JSON.stringify(response.error)}`);
|
|
924
934
|
}
|
|
@@ -964,7 +974,7 @@ export class OpenCodeAgentClient {
|
|
|
964
974
|
const directory = options.cwd;
|
|
965
975
|
const client = this.runtime.createClient({ baseUrl: url, directory });
|
|
966
976
|
try {
|
|
967
|
-
const response = await withTimeout(client.app.agents({ directory }), 10000, "OpenCode app.agents timed out after 10s");
|
|
977
|
+
const response = await openCodeMetadataLimit(() => withTimeout(client.app.agents({ directory }), 10000, "OpenCode app.agents timed out after 10s"));
|
|
968
978
|
if (response.error || !response.data) {
|
|
969
979
|
return DEFAULT_MODES;
|
|
970
980
|
}
|
|
@@ -1102,7 +1112,7 @@ export class OpenCodeAgentClient {
|
|
|
1102
1112
|
return normalizeOpenCodeConfig({ ...config, provider: "opencode" });
|
|
1103
1113
|
}
|
|
1104
1114
|
async populateModelContextWindowCache(client, cwd) {
|
|
1105
|
-
const response = await client.provider.list({ directory: cwd });
|
|
1115
|
+
const response = await openCodeMetadataLimit(() => client.provider.list({ directory: cwd }));
|
|
1106
1116
|
if (response.error || !response.data) {
|
|
1107
1117
|
return;
|
|
1108
1118
|
}
|
|
@@ -1794,6 +1804,7 @@ function appendOpenCodeQuestionAsked(event, state, events) {
|
|
|
1794
1804
|
header: q.header,
|
|
1795
1805
|
options,
|
|
1796
1806
|
...(q.multiple === true ? { multiSelect: true } : {}),
|
|
1807
|
+
allowOther: true,
|
|
1797
1808
|
},
|
|
1798
1809
|
];
|
|
1799
1810
|
});
|
|
@@ -2467,9 +2478,9 @@ class OpenCodeAgentSession {
|
|
|
2467
2478
|
if (this.availableModesCache) {
|
|
2468
2479
|
return this.availableModesCache;
|
|
2469
2480
|
}
|
|
2470
|
-
const response = await this.client.app.agents({
|
|
2481
|
+
const response = await openCodeMetadataLimit(() => this.client.app.agents({
|
|
2471
2482
|
directory: this.config.cwd,
|
|
2472
|
-
});
|
|
2483
|
+
}));
|
|
2473
2484
|
const agents = response.error || !response.data ? [] : response.data;
|
|
2474
2485
|
const discoveredModes = agents.filter(isSelectableOpenCodeAgent).map(mapOpenCodeAgentToMode);
|
|
2475
2486
|
this.availableModesCache = mergeOpenCodeModes(discoveredModes);
|
|
@@ -270,7 +270,36 @@ export function selectProjectedTimelinePage(input) {
|
|
|
270
270
|
const limit = input.limit === undefined ? 0 : Math.max(0, Math.floor(input.limit));
|
|
271
271
|
const bounds = input.bounds ?? getTimelineBounds(input.rows);
|
|
272
272
|
const projectedAll = projectTimelineRows({ rows: input.rows, mode: "projected" });
|
|
273
|
-
if (
|
|
273
|
+
if (!bounds) {
|
|
274
|
+
return {
|
|
275
|
+
entries: [],
|
|
276
|
+
startSeq: null,
|
|
277
|
+
endSeq: null,
|
|
278
|
+
hasOlder: false,
|
|
279
|
+
hasNewer: false,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
if (projectedAll.length === 0) {
|
|
283
|
+
if (input.direction === "after") {
|
|
284
|
+
const cursorSeq = input.cursorSeq ?? bounds.minSeq - 1;
|
|
285
|
+
return {
|
|
286
|
+
entries: [],
|
|
287
|
+
startSeq: null,
|
|
288
|
+
endSeq: null,
|
|
289
|
+
hasOlder: cursorSeq >= bounds.minSeq,
|
|
290
|
+
hasNewer: cursorSeq < bounds.maxSeq,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (input.direction === "before") {
|
|
294
|
+
const cursorSeq = input.cursorSeq ?? bounds.maxSeq + 1;
|
|
295
|
+
return {
|
|
296
|
+
entries: [],
|
|
297
|
+
startSeq: null,
|
|
298
|
+
endSeq: null,
|
|
299
|
+
hasOlder: cursorSeq > bounds.minSeq,
|
|
300
|
+
hasNewer: cursorSeq <= bounds.maxSeq,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
274
303
|
return {
|
|
275
304
|
entries: [],
|
|
276
305
|
startSeq: null,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export async function writeFileAtomic(filePath, data) {
|
|
5
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
6
|
+
const tempPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`);
|
|
7
|
+
try {
|
|
8
|
+
await fs.writeFile(tempPath, data, "utf8");
|
|
9
|
+
await fs.rename(tempPath, filePath);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
await fs.rm(tempPath, { force: true });
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function writeJsonFileAtomic(filePath, value) {
|
|
17
|
+
await writeFileAtomic(filePath, JSON.stringify(value, null, 2));
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=atomic-file.js.map
|
|
@@ -34,7 +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 || snapshot.git.aheadOfOrigin === null) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (snapshot.git.aheadOfOrigin > 0) {
|
|
38
41
|
return;
|
|
39
42
|
}
|
|
40
43
|
const ownership = await deps.isPaseoOwnedWorktreeCwd(cwd, {
|
|
@@ -418,6 +418,7 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
418
418
|
paseoHome: config.paseoHome,
|
|
419
419
|
logger,
|
|
420
420
|
agentManager,
|
|
421
|
+
providerSnapshotManager,
|
|
421
422
|
});
|
|
422
423
|
await loopService.initialize();
|
|
423
424
|
logger.info({ elapsed: elapsed() }, "Loop service initialized");
|
|
@@ -426,6 +427,7 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
426
427
|
logger,
|
|
427
428
|
agentManager,
|
|
428
429
|
agentStorage,
|
|
430
|
+
providerSnapshotManager,
|
|
429
431
|
});
|
|
430
432
|
await scheduleService.start();
|
|
431
433
|
agentManager.setAgentArchivedCallback(async (agentId) => {
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { writeJsonFileAtomic } from "../atomic-file.js";
|
|
5
6
|
import { ChatMessageSchema, ChatRoomDetailSchema, ChatRoomSchema, } from "@getpaseo/protocol/chat/types";
|
|
6
7
|
const ChatStorePayloadSchema = z.object({
|
|
7
8
|
rooms: z.array(ChatRoomSchema),
|
|
@@ -246,10 +247,7 @@ export class FileBackedChatService {
|
|
|
246
247
|
.flat()
|
|
247
248
|
.sort((left, right) => left.createdAt.localeCompare(right.createdAt)),
|
|
248
249
|
};
|
|
249
|
-
await
|
|
250
|
-
const tempPath = `${this.filePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
251
|
-
await fs.writeFile(tempPath, JSON.stringify(payload, null, 2), "utf8");
|
|
252
|
-
await fs.rename(tempPath, this.filePath);
|
|
250
|
+
await writeJsonFileAtomic(this.filePath, payload);
|
|
253
251
|
}
|
|
254
252
|
findRoomByName(name) {
|
|
255
253
|
const normalizedName = normalizeRoomName(name);
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { generateKeyPair, exportPublicKey, exportSecretKey, importPublicKey, importSecretKey, } from "@getpaseo/relay/e2ee";
|
|
5
|
-
import { ensurePrivateFile,
|
|
5
|
+
import { ensurePrivateFile, writePrivateFileAtomicSync } from "./private-files.js";
|
|
6
6
|
const KeyPairSchema = z.object({
|
|
7
7
|
v: z.literal(2),
|
|
8
8
|
publicKeyB64: z.string().min(1),
|
|
@@ -35,7 +35,7 @@ export async function loadOrCreateDaemonKeyPair(paseoHome, logger) {
|
|
|
35
35
|
publicKeyB64,
|
|
36
36
|
secretKeyB64,
|
|
37
37
|
};
|
|
38
|
-
|
|
38
|
+
writePrivateFileAtomicSync(filePath, JSON.stringify(payload, null, 2) + "\n");
|
|
39
39
|
log?.info({ filePath }, "Saved daemon keypair");
|
|
40
40
|
return { keyPair, publicKeyB64 };
|
|
41
41
|
}
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import type { Logger } from "pino";
|
|
3
3
|
import type { AgentManager } from "./agent/agent-manager.js";
|
|
4
4
|
import type { AgentProvider } from "./agent/agent-sdk-types.js";
|
|
5
|
+
import type { ProviderSnapshotManager } from "./agent/provider-snapshot-manager.js";
|
|
5
6
|
declare const LoopLogEntrySchema: z.ZodObject<{
|
|
6
7
|
seq: z.ZodNumber;
|
|
7
8
|
timestamp: z.ZodString;
|
|
@@ -493,6 +494,7 @@ export interface LoopLogsResult {
|
|
|
493
494
|
entries: LoopLogEntry[];
|
|
494
495
|
nextCursor: number;
|
|
495
496
|
}
|
|
497
|
+
type CreateConfigResolver = Pick<ProviderSnapshotManager, "resolveCreateConfig">;
|
|
496
498
|
export declare class LoopService {
|
|
497
499
|
private readonly options;
|
|
498
500
|
private readonly storePath;
|
|
@@ -505,6 +507,7 @@ export declare class LoopService {
|
|
|
505
507
|
paseoHome: string;
|
|
506
508
|
agentManager: AgentManager;
|
|
507
509
|
logger: Logger;
|
|
510
|
+
providerSnapshotManager: CreateConfigResolver;
|
|
508
511
|
});
|
|
509
512
|
initialize(): Promise<void>;
|
|
510
513
|
runLoop(input: LoopRunOptions): Promise<LoopRecord>;
|
|
@@ -518,6 +521,7 @@ export declare class LoopService {
|
|
|
518
521
|
private runVerification;
|
|
519
522
|
private buildWorkerConfig;
|
|
520
523
|
private buildVerifierConfig;
|
|
524
|
+
private resolveProviderCreateConfig;
|
|
521
525
|
private resolveFinalText;
|
|
522
526
|
private toPrompt;
|
|
523
527
|
private finishLoop;
|
|
@@ -2,10 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { writeJsonFileAtomic } from "./atomic-file.js";
|
|
5
6
|
import { curateAgentActivity } from "./agent/activity-curator.js";
|
|
6
7
|
import { getStructuredAgentResponse } from "./agent/agent-response-loop.js";
|
|
7
8
|
import { execCommand, platformShell } from "../utils/spawn.js";
|
|
8
|
-
import { getUnattendedModeId } from "@getpaseo/protocol/provider-manifest";
|
|
9
9
|
const LOOP_ID_LENGTH = 8;
|
|
10
10
|
const DEFAULT_LOOP_PROVIDER = "claude";
|
|
11
11
|
const MAX_VERIFY_OUTPUT_BYTES = 64 * 1024;
|
|
@@ -494,7 +494,7 @@ export class LoopService {
|
|
|
494
494
|
await this.persist();
|
|
495
495
|
}
|
|
496
496
|
async runWorkerIteration(loop, iteration, signal) {
|
|
497
|
-
const agent = await this.options.agentManager.createAgent(this.buildWorkerConfig(loop, iteration));
|
|
497
|
+
const agent = await this.options.agentManager.createAgent(await this.buildWorkerConfig(loop, iteration));
|
|
498
498
|
iteration.workerAgentId = agent.id;
|
|
499
499
|
loop.activeWorkerAgentId = agent.id;
|
|
500
500
|
loop.updatedAt = nowIso();
|
|
@@ -591,7 +591,7 @@ export class LoopService {
|
|
|
591
591
|
return true;
|
|
592
592
|
}
|
|
593
593
|
const startedAt = nowIso();
|
|
594
|
-
const verifierAgent = await this.options.agentManager.createAgent(this.buildVerifierConfig(loop, iteration));
|
|
594
|
+
const verifierAgent = await this.options.agentManager.createAgent(await this.buildVerifierConfig(loop, iteration));
|
|
595
595
|
iteration.verifierAgentId = verifierAgent.id;
|
|
596
596
|
loop.activeVerifierAgentId = verifierAgent.id;
|
|
597
597
|
loop.updatedAt = nowIso();
|
|
@@ -659,28 +659,47 @@ export class LoopService {
|
|
|
659
659
|
}
|
|
660
660
|
}
|
|
661
661
|
}
|
|
662
|
-
buildWorkerConfig(loop, iteration) {
|
|
662
|
+
async buildWorkerConfig(loop, iteration) {
|
|
663
663
|
const provider = loop.workerProvider ?? loop.provider;
|
|
664
|
+
const resolvedUnattendedConfig = loop.modeId
|
|
665
|
+
? { modeId: loop.modeId, featureValues: undefined }
|
|
666
|
+
: await this.resolveProviderCreateConfig({ provider, cwd: loop.cwd });
|
|
664
667
|
return {
|
|
665
668
|
provider,
|
|
666
669
|
cwd: loop.cwd,
|
|
667
670
|
model: loop.workerModel ?? loop.model ?? undefined,
|
|
668
|
-
modeId:
|
|
671
|
+
modeId: resolvedUnattendedConfig.modeId,
|
|
672
|
+
featureValues: resolvedUnattendedConfig.featureValues,
|
|
669
673
|
title: buildWorkerTitle(loop, iteration.index),
|
|
670
674
|
internal: true,
|
|
671
675
|
};
|
|
672
676
|
}
|
|
673
|
-
buildVerifierConfig(loop, iteration) {
|
|
677
|
+
async buildVerifierConfig(loop, iteration) {
|
|
674
678
|
const provider = loop.verifierProvider ?? loop.provider;
|
|
679
|
+
const explicitModeId = loop.verifierModeId ?? loop.modeId;
|
|
680
|
+
const resolvedUnattendedConfig = explicitModeId
|
|
681
|
+
? { modeId: explicitModeId, featureValues: undefined }
|
|
682
|
+
: await this.resolveProviderCreateConfig({ provider, cwd: loop.cwd });
|
|
675
683
|
return {
|
|
676
684
|
provider,
|
|
677
685
|
cwd: loop.cwd,
|
|
678
686
|
model: loop.verifierModel ?? loop.model ?? undefined,
|
|
679
|
-
modeId:
|
|
687
|
+
modeId: resolvedUnattendedConfig.modeId,
|
|
688
|
+
featureValues: resolvedUnattendedConfig.featureValues,
|
|
680
689
|
title: buildVerifierTitle(loop, iteration.index),
|
|
681
690
|
internal: true,
|
|
682
691
|
};
|
|
683
692
|
}
|
|
693
|
+
resolveProviderCreateConfig(input) {
|
|
694
|
+
return this.options.providerSnapshotManager.resolveCreateConfig({
|
|
695
|
+
provider: input.provider,
|
|
696
|
+
cwd: input.cwd,
|
|
697
|
+
requestedMode: undefined,
|
|
698
|
+
featureValues: undefined,
|
|
699
|
+
parent: null,
|
|
700
|
+
unattended: true,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
684
703
|
resolveFinalText(timeline, finalText) {
|
|
685
704
|
if (finalText.trim()) {
|
|
686
705
|
return finalText;
|
|
@@ -739,9 +758,8 @@ export class LoopService {
|
|
|
739
758
|
}
|
|
740
759
|
async persist() {
|
|
741
760
|
const nextPersist = this.persistQueue.then(async () => {
|
|
742
|
-
await fs.mkdir(path.dirname(this.storePath), { recursive: true });
|
|
743
761
|
const records = Array.from(this.loops.values()).sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
744
|
-
await
|
|
762
|
+
await writeJsonFileAtomic(this.storePath, records);
|
|
745
763
|
return;
|
|
746
764
|
});
|
|
747
765
|
this.persistQueue = nextPersist.catch(() => { });
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { AgentProviderRuntimeSettingsMapSchema, migrateProviderSettings, ProviderOverridesSchema, } from "./agent/provider-launch-config.js";
|
|
5
|
-
import { ensurePrivateFile,
|
|
5
|
+
import { ensurePrivateFile, writePrivateFileAtomicSync } from "./private-files.js";
|
|
6
6
|
export const LogLevelSchema = z.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
|
|
7
7
|
export const LogFormatSchema = z.enum(["pretty", "json"]);
|
|
8
8
|
const LogConfigSchema = z
|
|
@@ -296,7 +296,7 @@ export function loadPersistedConfig(paseoHome, logger) {
|
|
|
296
296
|
const configPath = getConfigPath(paseoHome);
|
|
297
297
|
if (!existsSync(configPath)) {
|
|
298
298
|
try {
|
|
299
|
-
|
|
299
|
+
writePrivateFileAtomicSync(configPath, JSON.stringify(DEFAULT_PERSISTED_CONFIG, null, 2) + "\n");
|
|
300
300
|
log?.info(`Initialized config file at ${configPath}`);
|
|
301
301
|
}
|
|
302
302
|
catch (err) {
|
|
@@ -347,7 +347,7 @@ export function savePersistedConfig(paseoHome, config, logger) {
|
|
|
347
347
|
throw new Error(`[Config] Invalid config to save:\n${issues}`);
|
|
348
348
|
}
|
|
349
349
|
try {
|
|
350
|
-
|
|
350
|
+
writePrivateFileAtomicSync(configPath, JSON.stringify(result.data, null, 2) + "\n");
|
|
351
351
|
log?.info(`Saved to ${configPath}`);
|
|
352
352
|
}
|
|
353
353
|
catch (err) {
|
|
@@ -2,6 +2,5 @@ export declare const PRIVATE_DIRECTORY_MODE = 448;
|
|
|
2
2
|
export declare const PRIVATE_FILE_MODE = 384;
|
|
3
3
|
export declare function ensurePrivateDirectory(directoryPath: string): void;
|
|
4
4
|
export declare function ensurePrivateFile(filePath: string): void;
|
|
5
|
-
export declare function writePrivateFileSync(filePath: string, data: string | NodeJS.ArrayBufferView): void;
|
|
6
5
|
export declare function writePrivateFileAtomicSync(filePath: string, data: string | NodeJS.ArrayBufferView): void;
|
|
7
6
|
//# sourceMappingURL=private-files.d.ts.map
|
|
@@ -21,11 +21,6 @@ export function ensurePrivateDirectory(directoryPath) {
|
|
|
21
21
|
export function ensurePrivateFile(filePath) {
|
|
22
22
|
chmodBestEffort(filePath, PRIVATE_FILE_MODE);
|
|
23
23
|
}
|
|
24
|
-
export function writePrivateFileSync(filePath, data) {
|
|
25
|
-
ensurePrivateDirectory(path.dirname(filePath));
|
|
26
|
-
writeFileSync(filePath, data, { mode: PRIVATE_FILE_MODE });
|
|
27
|
-
ensurePrivateFile(filePath);
|
|
28
|
-
}
|
|
29
24
|
export function writePrivateFileAtomicSync(filePath, data) {
|
|
30
25
|
ensurePrivateDirectory(path.dirname(filePath));
|
|
31
26
|
const tmpPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { Logger } from "pino";
|
|
2
2
|
import { AgentManager } from "../agent/agent-manager.js";
|
|
3
3
|
import type { AgentStorage } from "../agent/agent-storage.js";
|
|
4
|
+
import type { ProviderSnapshotManager } from "../agent/provider-snapshot-manager.js";
|
|
4
5
|
import type { CreateScheduleInput, ScheduleExecutionResult, ScheduleRun, StoredSchedule, UpdateScheduleInput } from "@getpaseo/protocol/schedule/types";
|
|
6
|
+
type CreateConfigResolver = Pick<ProviderSnapshotManager, "resolveCreateConfig">;
|
|
5
7
|
export interface ScheduleServiceOptions {
|
|
6
8
|
paseoHome: string;
|
|
7
9
|
logger: Logger;
|
|
8
10
|
agentManager: AgentManager;
|
|
9
11
|
agentStorage: AgentStorage;
|
|
12
|
+
providerSnapshotManager: CreateConfigResolver;
|
|
10
13
|
now?: () => Date;
|
|
11
14
|
runner?: (schedule: StoredSchedule, runId: string) => Promise<ScheduleExecutionResult>;
|
|
12
15
|
}
|
|
@@ -15,6 +18,7 @@ export declare class ScheduleService {
|
|
|
15
18
|
private readonly logger;
|
|
16
19
|
private readonly agentManager;
|
|
17
20
|
private readonly agentStorage;
|
|
21
|
+
private readonly createConfigResolver;
|
|
18
22
|
private readonly now;
|
|
19
23
|
private readonly runner;
|
|
20
24
|
private readonly runningScheduleIds;
|
|
@@ -37,5 +41,7 @@ export declare class ScheduleService {
|
|
|
37
41
|
private runSchedule;
|
|
38
42
|
private finishRun;
|
|
39
43
|
private executeSchedule;
|
|
44
|
+
private resolveProviderCreateConfig;
|
|
40
45
|
}
|
|
46
|
+
export {};
|
|
41
47
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { curateAgentActivity } from "../agent/activity-curator.js";
|
|
4
4
|
import { ensureAgentLoaded } from "../agent/agent-loading.js";
|
|
5
5
|
import { formatSystemNotificationPrompt } from "../agent/agent-prompt.js";
|
|
6
|
-
import {
|
|
6
|
+
import { resolveCreateAgentTitles } from "../agent/create-agent-title.js";
|
|
7
7
|
import { ScheduleStore } from "./store.js";
|
|
8
8
|
import { computeNextRunAt, validateScheduleCadence } from "./cron.js";
|
|
9
9
|
const SCHEDULE_TICK_INTERVAL_MS = 1000;
|
|
@@ -113,6 +113,7 @@ export class ScheduleService {
|
|
|
113
113
|
this.logger = options.logger.child({ module: "schedule-service" });
|
|
114
114
|
this.agentManager = options.agentManager;
|
|
115
115
|
this.agentStorage = options.agentStorage;
|
|
116
|
+
this.createConfigResolver = options.providerSnapshotManager;
|
|
116
117
|
this.now = options.now ?? (() => new Date());
|
|
117
118
|
this.runner = options.runner ?? ((schedule, runId) => this.executeSchedule(schedule, runId));
|
|
118
119
|
}
|
|
@@ -418,8 +419,8 @@ export class ScheduleService {
|
|
|
418
419
|
await this.store.put(updated);
|
|
419
420
|
}
|
|
420
421
|
async executeSchedule(schedule, runId) {
|
|
421
|
-
const wrappedPrompt = formatSystemNotificationPrompt(buildScheduleFireBody(schedule, runId));
|
|
422
422
|
if (schedule.target.type === "agent") {
|
|
423
|
+
const wrappedPrompt = formatSystemNotificationPrompt(buildScheduleFireBody(schedule, runId));
|
|
423
424
|
const record = await this.agentStorage.get(schedule.target.agentId);
|
|
424
425
|
if (record?.archivedAt) {
|
|
425
426
|
throw new Error(`Agent ${schedule.target.agentId} is archived`);
|
|
@@ -443,30 +444,49 @@ export class ScheduleService {
|
|
|
443
444
|
}),
|
|
444
445
|
};
|
|
445
446
|
}
|
|
447
|
+
const targetConfig = schedule.target.config;
|
|
448
|
+
const resolvedUnattendedConfig = targetConfig.modeId
|
|
449
|
+
? { modeId: targetConfig.modeId, featureValues: targetConfig.featureValues }
|
|
450
|
+
: await this.resolveProviderCreateConfig({
|
|
451
|
+
provider: targetConfig.provider,
|
|
452
|
+
cwd: targetConfig.cwd,
|
|
453
|
+
requestedMode: undefined,
|
|
454
|
+
featureValues: targetConfig.featureValues,
|
|
455
|
+
parent: null,
|
|
456
|
+
unattended: true,
|
|
457
|
+
});
|
|
446
458
|
const config = {
|
|
447
|
-
provider:
|
|
448
|
-
cwd:
|
|
449
|
-
modeId:
|
|
450
|
-
model:
|
|
451
|
-
thinkingOptionId:
|
|
452
|
-
title:
|
|
453
|
-
approvalPolicy:
|
|
454
|
-
sandboxMode:
|
|
455
|
-
networkAccess:
|
|
456
|
-
webSearch:
|
|
457
|
-
featureValues:
|
|
458
|
-
extra:
|
|
459
|
-
systemPrompt:
|
|
460
|
-
mcpServers:
|
|
459
|
+
provider: targetConfig.provider,
|
|
460
|
+
cwd: targetConfig.cwd,
|
|
461
|
+
modeId: resolvedUnattendedConfig.modeId,
|
|
462
|
+
model: targetConfig.model,
|
|
463
|
+
thinkingOptionId: targetConfig.thinkingOptionId,
|
|
464
|
+
title: targetConfig.title,
|
|
465
|
+
approvalPolicy: targetConfig.approvalPolicy,
|
|
466
|
+
sandboxMode: targetConfig.sandboxMode,
|
|
467
|
+
networkAccess: targetConfig.networkAccess,
|
|
468
|
+
webSearch: targetConfig.webSearch,
|
|
469
|
+
featureValues: resolvedUnattendedConfig.featureValues,
|
|
470
|
+
extra: targetConfig.extra,
|
|
471
|
+
systemPrompt: targetConfig.systemPrompt,
|
|
472
|
+
mcpServers: targetConfig.mcpServers,
|
|
461
473
|
};
|
|
474
|
+
const { provisionalTitle } = resolveCreateAgentTitles({
|
|
475
|
+
configTitle: config.title,
|
|
476
|
+
initialPrompt: schedule.prompt,
|
|
477
|
+
});
|
|
462
478
|
const labels = {
|
|
463
479
|
"paseo.schedule-id": schedule.id,
|
|
464
480
|
"paseo.schedule-run": runId,
|
|
465
481
|
};
|
|
466
|
-
const agent = await this.agentManager.createAgent(config, undefined, {
|
|
482
|
+
const agent = await this.agentManager.createAgent(config, undefined, {
|
|
483
|
+
labels,
|
|
484
|
+
initialPrompt: schedule.prompt,
|
|
485
|
+
initialTitle: provisionalTitle,
|
|
486
|
+
});
|
|
467
487
|
let result;
|
|
468
488
|
try {
|
|
469
|
-
result = await this.agentManager.runAgent(agent.id,
|
|
489
|
+
result = await this.agentManager.runAgent(agent.id, schedule.prompt);
|
|
470
490
|
}
|
|
471
491
|
catch (error) {
|
|
472
492
|
try {
|
|
@@ -488,5 +508,8 @@ export class ScheduleService {
|
|
|
488
508
|
}),
|
|
489
509
|
};
|
|
490
510
|
}
|
|
511
|
+
async resolveProviderCreateConfig(input) {
|
|
512
|
+
return this.createConfigResolver.resolveCreateConfig(input);
|
|
513
|
+
}
|
|
491
514
|
}
|
|
492
515
|
//# sourceMappingURL=service.js.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { mkdir, readFile, readdir, rm
|
|
2
|
+
import { mkdir, readFile, readdir, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { StoredScheduleSchema } from "@getpaseo/protocol/schedule/types";
|
|
5
|
+
import { writeJsonFileAtomic } from "../atomic-file.js";
|
|
5
6
|
function generateScheduleId() {
|
|
6
7
|
return randomBytes(4).toString("hex");
|
|
7
8
|
}
|
|
@@ -46,7 +47,7 @@ export class ScheduleStore {
|
|
|
46
47
|
}
|
|
47
48
|
async put(schedule) {
|
|
48
49
|
await this.ensureDir();
|
|
49
|
-
await
|
|
50
|
+
await writeJsonFileAtomic(this.filePath(schedule.id), schedule);
|
|
50
51
|
}
|
|
51
52
|
async delete(id) {
|
|
52
53
|
await this.ensureDir();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
|
-
import { ensurePrivateFile,
|
|
4
|
+
import { ensurePrivateFile, writePrivateFileAtomicSync } from "./private-files.js";
|
|
5
5
|
const SERVER_ID_FILENAME = "server-id";
|
|
6
6
|
function getLogger(logger) {
|
|
7
7
|
return logger?.child({ module: "server-id" });
|
|
@@ -31,7 +31,7 @@ export function getOrCreateServerId(paseoHome, options) {
|
|
|
31
31
|
// Persist the override for consistent identity across restarts.
|
|
32
32
|
if (!existsSync(serverIdPath)) {
|
|
33
33
|
try {
|
|
34
|
-
|
|
34
|
+
writePrivateFileAtomicSync(serverIdPath, `${envOverride}\n`);
|
|
35
35
|
log?.info({ serverId: envOverride }, "Persisted PASEO_SERVER_ID override");
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
@@ -58,7 +58,7 @@ export function getOrCreateServerId(paseoHome, options) {
|
|
|
58
58
|
}
|
|
59
59
|
const created = generateServerId();
|
|
60
60
|
try {
|
|
61
|
-
|
|
61
|
+
writePrivateFileAtomicSync(serverIdPath, `${created}\n`);
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
64
|
log?.warn({ error }, "Failed to persist serverId (continuing with in-memory id)");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ClientCapability } from "@getpaseo/protocol/client-capabilities";
|
|
2
|
-
import { type AgentSnapshotPayload, type SessionInboundMessage, type SessionOutboundMessage, type
|
|
2
|
+
import { type AgentSnapshotPayload, type SessionInboundMessage, type SessionOutboundMessage, type WorkspaceSetupSnapshot } from "./messages.js";
|
|
3
3
|
import type { TerminalManager } from "../terminal/terminal-manager.js";
|
|
4
4
|
import { type TerminalStreamFrame } from "@getpaseo/protocol/binary-frames/index";
|
|
5
5
|
import type { SpeechToTextProvider, TextToSpeechProvider } from "./speech/speech-provider.js";
|
|
@@ -176,8 +176,6 @@ export declare class Session {
|
|
|
176
176
|
private readonly terminalController;
|
|
177
177
|
private inflightRequests;
|
|
178
178
|
private peakInflightRequests;
|
|
179
|
-
private readonly availableEditorTargetsCache;
|
|
180
|
-
private readonly getMemoizedAvailableEditorTargets;
|
|
181
179
|
private readonly checkoutDiffSubscriptions;
|
|
182
180
|
private readonly workspaceGitWatchTargets;
|
|
183
181
|
private readonly workspaceSetupSnapshots;
|
|
@@ -254,7 +252,6 @@ export declare class Session {
|
|
|
254
252
|
private buildAgentPayload;
|
|
255
253
|
private buildStoredAgentPayload;
|
|
256
254
|
private isProviderVisibleToClient;
|
|
257
|
-
private filterEditorsForClient;
|
|
258
255
|
private agentThinkingOptionMatchesFilter;
|
|
259
256
|
private matchesAgentStructuralFilter;
|
|
260
257
|
private matchesAgentFilter;
|
|
@@ -471,27 +468,20 @@ export declare class Session {
|
|
|
471
468
|
private handleFetchAgentHistory;
|
|
472
469
|
private handleFetchRecentProviderSessions;
|
|
473
470
|
private handleFetchWorkspacesRequest;
|
|
471
|
+
private buildBootstrapSnapshot;
|
|
474
472
|
private registerWorkspaceForImportedAgent;
|
|
475
473
|
private handleOpenProjectRequest;
|
|
476
474
|
private buildWorkspaceScriptPayloadSnapshot;
|
|
477
475
|
private resolveWorkspaceScriptGitMetadata;
|
|
478
476
|
private emitWorkspaceScriptStatusUpdate;
|
|
479
|
-
resolveAvailableEditorTargets(): Promise<EditorTargetDescriptorPayload[]>;
|
|
480
|
-
getAvailableEditorTargets(): Promise<{
|
|
481
|
-
id: string;
|
|
482
|
-
label: string;
|
|
483
|
-
}[]>;
|
|
484
|
-
openEditorTarget(options: {
|
|
485
|
-
editorId: EditorTargetId;
|
|
486
|
-
path: string;
|
|
487
|
-
}): Promise<void>;
|
|
488
477
|
private handleStartWorkspaceScriptRequest;
|
|
489
|
-
private
|
|
490
|
-
private
|
|
478
|
+
private handleLegacyListAvailableEditorsRequest;
|
|
479
|
+
private handleLegacyOpenInEditorRequest;
|
|
491
480
|
private handleCreatePaseoWorktreeRequest;
|
|
492
481
|
private createPaseoWorktreeWorkflow;
|
|
493
482
|
private handleWorkspaceSetupStatusRequest;
|
|
494
483
|
private handleArchiveWorkspaceRequest;
|
|
484
|
+
private handleWorkspaceClearAttentionRequest;
|
|
495
485
|
private handleFetchAgent;
|
|
496
486
|
private shouldUseFullTimelineForProjectedPage;
|
|
497
487
|
private selectCanonicalTimelineProjection;
|