@getpaseo/server 0.1.84 → 0.1.86
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-entrypoint.js +1 -0
- package/dist/server/server/agent/agent-manager.d.ts +4 -0
- package/dist/server/server/agent/agent-manager.js +23 -0
- package/dist/server/server/agent/agent-metadata-generator.d.ts +9 -0
- package/dist/server/server/agent/agent-metadata-generator.js +11 -2
- package/dist/server/server/agent/agent-response-loop.d.ts +1 -1
- package/dist/server/server/agent/agent-response-loop.js +3 -13
- package/dist/server/server/agent/create-agent/create.d.ts +2 -0
- package/dist/server/server/agent/create-agent/create.js +7 -0
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +0 -1
- package/dist/server/server/agent/import-sessions.d.ts +3 -0
- package/dist/server/server/agent/import-sessions.js +11 -0
- package/dist/server/server/agent/mcp-server.d.ts +0 -1
- package/dist/server/server/agent/mcp-server.js +0 -4
- package/dist/server/server/agent/providers/claude/agent.d.ts +2 -1
- package/dist/server/server/agent/providers/claude/agent.js +70 -0
- package/dist/server/server/agent/providers/claude/feature-definitions.d.ts +8 -0
- package/dist/server/server/agent/providers/claude/feature-definitions.js +36 -0
- package/dist/server/server/agent/providers/claude/models.js +19 -5
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +6 -3
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +5 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js +62 -0
- package/dist/server/server/agent/structured-generation-providers.d.ts +29 -0
- package/dist/server/server/agent/structured-generation-providers.js +192 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +0 -2
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +0 -1
- package/dist/server/server/bootstrap.d.ts +7 -0
- package/dist/server/server/bootstrap.js +11 -2
- package/dist/server/server/config.js +1 -0
- package/dist/server/server/daemon-config-store.js +46 -6
- package/dist/server/server/daemon-worker.js +1 -0
- package/dist/server/server/file-explorer/service.js +4 -4
- package/dist/server/server/paseo-worktree-archive-service.d.ts +2 -6
- package/dist/server/server/paseo-worktree-archive-service.js +13 -33
- package/dist/server/server/persisted-config.d.ts +77 -22
- package/dist/server/server/persisted-config.js +13 -0
- package/dist/server/server/schedule/service.d.ts +1 -0
- package/dist/server/server/schedule/service.js +15 -0
- package/dist/server/server/session.d.ts +3 -2
- package/dist/server/server/session.js +77 -25
- package/dist/server/server/speech/providers/local/runtime.js +52 -133
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +9 -2
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +7 -0
- package/dist/server/server/speech/providers/local/worker-bytes.d.ts +4 -0
- package/dist/server/server/speech/providers/local/worker-bytes.js +9 -0
- package/dist/server/server/speech/providers/local/worker-client.d.ts +80 -0
- package/dist/server/server/speech/providers/local/worker-client.js +438 -0
- package/dist/server/server/speech/providers/local/worker-process.d.ts +2 -0
- package/dist/server/server/speech/providers/local/worker-process.js +270 -0
- package/dist/server/server/speech/providers/local/worker-protocol.d.ts +95 -0
- package/dist/server/server/speech/providers/local/worker-protocol.js +2 -0
- package/dist/server/server/websocket-server.js +2 -0
- package/dist/server/server/worktree-branch-name-generator.d.ts +9 -0
- package/dist/server/server/worktree-branch-name-generator.js +11 -2
- package/dist/server/utils/worktree.d.ts +1 -1
- package/dist/server/utils/worktree.js +2 -2
- package/dist/src/server/persisted-config.js +13 -0
- package/package.json +5 -10
- package/dist/server/utils/branch-slug.d.ts +0 -14
- package/dist/server/utils/branch-slug.js +0 -49
|
@@ -112,6 +112,68 @@ export function flattenReadContent(value) {
|
|
|
112
112
|
}
|
|
113
113
|
return (nonEmptyString(value.text) ?? nonEmptyString(value.content) ?? nonEmptyString(value.output));
|
|
114
114
|
}
|
|
115
|
+
// Claude's Read tool returns `cat -n`-style content: each line prefixed with a
|
|
116
|
+
// right-aligned line number and a tab (`␣␣␣1\timport ...`). Other providers
|
|
117
|
+
// return raw source. We strip the gutter here so `read.content` is uniformly
|
|
118
|
+
// raw source across providers, and surface the first line number as `offset`
|
|
119
|
+
// so the client can rebuild the gutter itself. Guarded tightly (first line must
|
|
120
|
+
// match, near-total match ratio, strictly sequential numbering) so real source
|
|
121
|
+
// is never mistaken for a gutter.
|
|
122
|
+
const READ_GUTTER_LINE = /^\s*(\d+)\t(.*)$/;
|
|
123
|
+
export function stripReadLineNumberGutter(content) {
|
|
124
|
+
const text = nonEmptyString(content);
|
|
125
|
+
if (!text) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
129
|
+
const stripped = [];
|
|
130
|
+
let nonEmpty = 0;
|
|
131
|
+
let matched = 0;
|
|
132
|
+
let startLine;
|
|
133
|
+
let prevNumber;
|
|
134
|
+
let sequential = true;
|
|
135
|
+
let firstNonEmptyMatched = false;
|
|
136
|
+
let sawNonEmpty = false;
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
if (line.length === 0) {
|
|
139
|
+
stripped.push(line);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
nonEmpty += 1;
|
|
143
|
+
const match = line.match(READ_GUTTER_LINE);
|
|
144
|
+
if (!match) {
|
|
145
|
+
if (!sawNonEmpty) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
stripped.push(line);
|
|
149
|
+
sawNonEmpty = true;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (!sawNonEmpty) {
|
|
153
|
+
firstNonEmptyMatched = true;
|
|
154
|
+
}
|
|
155
|
+
sawNonEmpty = true;
|
|
156
|
+
matched += 1;
|
|
157
|
+
const lineNumber = Number.parseInt(match[1], 10);
|
|
158
|
+
if (startLine === undefined) {
|
|
159
|
+
startLine = lineNumber;
|
|
160
|
+
}
|
|
161
|
+
if (prevNumber !== undefined && lineNumber !== prevNumber + 1) {
|
|
162
|
+
sequential = false;
|
|
163
|
+
}
|
|
164
|
+
prevNumber = lineNumber;
|
|
165
|
+
stripped.push(match[2]);
|
|
166
|
+
}
|
|
167
|
+
if (!firstNonEmptyMatched || !sequential || nonEmpty === 0) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
// Sequential numbering from the first line is already a strong signal; the
|
|
171
|
+
// ratio only rejects source that has a couple of coincidental matches.
|
|
172
|
+
if (matched / nonEmpty < 0.5) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
return { content: stripped.join("\n"), startLine };
|
|
176
|
+
}
|
|
115
177
|
export function truncateDiffText(text, maxChars = 12000) {
|
|
116
178
|
if (typeof text !== "string") {
|
|
117
179
|
return undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentProvider } from "./agent-sdk-types.js";
|
|
2
|
+
import type { StructuredGenerationProvider } from "./agent-response-loop.js";
|
|
3
|
+
import type { ProviderSnapshotManager } from "./provider-snapshot-manager.js";
|
|
4
|
+
export interface StructuredGenerationDaemonConfig {
|
|
5
|
+
metadataGeneration?: {
|
|
6
|
+
providers?: Array<{
|
|
7
|
+
provider: string;
|
|
8
|
+
model?: string;
|
|
9
|
+
thinkingOptionId?: string;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface StructuredGenerationProviderIdentifier {
|
|
14
|
+
modelSubstring: string;
|
|
15
|
+
thinkingOptionId?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_STRUCTURED_GENERATION_PROVIDERS: readonly StructuredGenerationProviderIdentifier[];
|
|
18
|
+
export interface ResolveStructuredGenerationProvidersOptions {
|
|
19
|
+
cwd: string;
|
|
20
|
+
providerSnapshotManager: Pick<ProviderSnapshotManager, "listProviders">;
|
|
21
|
+
daemonConfig?: StructuredGenerationDaemonConfig | null;
|
|
22
|
+
currentSelection?: {
|
|
23
|
+
provider?: AgentProvider | null;
|
|
24
|
+
model?: string | null;
|
|
25
|
+
thinkingOptionId?: string | null;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export declare function resolveStructuredGenerationProviders(options: ResolveStructuredGenerationProvidersOptions): Promise<StructuredGenerationProvider[]>;
|
|
29
|
+
//# sourceMappingURL=structured-generation-providers.d.ts.map
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
export const DEFAULT_STRUCTURED_GENERATION_PROVIDERS = [
|
|
2
|
+
{ modelSubstring: "haiku" },
|
|
3
|
+
{ modelSubstring: "gpt-5.4-mini", thinkingOptionId: "low" },
|
|
4
|
+
{ modelSubstring: "minimax-m2.5" },
|
|
5
|
+
{ modelSubstring: "nemotron-3-super" },
|
|
6
|
+
];
|
|
7
|
+
export async function resolveStructuredGenerationProviders(options) {
|
|
8
|
+
const providerEntries = await options.providerSnapshotManager.listProviders({
|
|
9
|
+
cwd: options.cwd,
|
|
10
|
+
wait: true,
|
|
11
|
+
});
|
|
12
|
+
const enabledEntries = providerEntries.filter((entry) => entry.enabled);
|
|
13
|
+
const modelEntries = enabledEntries.filter((entry) => (entry.models?.length ?? 0) > 0);
|
|
14
|
+
const entriesByProvider = new Map(enabledEntries.map((entry) => [entry.provider, entry]));
|
|
15
|
+
const providers = [];
|
|
16
|
+
for (const configured of readConfiguredProviders(options.daemonConfig)) {
|
|
17
|
+
const resolvedConfigured = resolveConfiguredCandidate(configured, modelEntries, entriesByProvider);
|
|
18
|
+
if (!resolvedConfigured) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
providers.push(resolvedConfigured);
|
|
22
|
+
}
|
|
23
|
+
for (const identifier of DEFAULT_STRUCTURED_GENERATION_PROVIDERS) {
|
|
24
|
+
const resolved = resolveByModelSubstring(modelEntries, identifier);
|
|
25
|
+
if (resolved) {
|
|
26
|
+
providers.push(resolved);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const currentSelection = resolveCurrentSelection(options.currentSelection, modelEntries, entriesByProvider);
|
|
30
|
+
if (currentSelection) {
|
|
31
|
+
providers.push(currentSelection);
|
|
32
|
+
}
|
|
33
|
+
return dedupeProviders(providers);
|
|
34
|
+
}
|
|
35
|
+
function resolveCurrentSelection(selection, readyEntries, entriesByProvider) {
|
|
36
|
+
if (!selection) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const provider = selection.provider?.trim();
|
|
40
|
+
if (!provider) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const normalized = resolveConfiguredCandidate({
|
|
44
|
+
provider,
|
|
45
|
+
...(selection.model ? { model: selection.model } : {}),
|
|
46
|
+
...(selection.thinkingOptionId ? { thinkingOptionId: selection.thinkingOptionId } : {}),
|
|
47
|
+
}, readyEntries, entriesByProvider);
|
|
48
|
+
if (normalized) {
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
const explicitModel = selection.model?.trim();
|
|
52
|
+
if (explicitModel) {
|
|
53
|
+
return {
|
|
54
|
+
provider,
|
|
55
|
+
model: explicitModel,
|
|
56
|
+
...(selection.thinkingOptionId ? { thinkingOptionId: selection.thinkingOptionId } : {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const model = selectDefaultModel(entriesByProvider.get(provider)?.models ?? []);
|
|
60
|
+
if (!model) {
|
|
61
|
+
return { provider };
|
|
62
|
+
}
|
|
63
|
+
const thinkingOptionId = resolveThinkingOptionId(model, selection.thinkingOptionId);
|
|
64
|
+
return {
|
|
65
|
+
provider,
|
|
66
|
+
model: model.id,
|
|
67
|
+
...(thinkingOptionId ? { thinkingOptionId } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function resolveConfiguredCandidate(candidate, readyEntries, entriesByProvider) {
|
|
71
|
+
const provider = candidate.provider.trim();
|
|
72
|
+
if (!provider) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const topLevelEntry = entriesByProvider.get(provider);
|
|
76
|
+
const configuredModel = candidate.model?.trim();
|
|
77
|
+
if (topLevelEntry) {
|
|
78
|
+
if (configuredModel) {
|
|
79
|
+
return {
|
|
80
|
+
provider,
|
|
81
|
+
model: configuredModel,
|
|
82
|
+
...(candidate.thinkingOptionId ? { thinkingOptionId: candidate.thinkingOptionId } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const model = selectDefaultModel(topLevelEntry.models ?? []);
|
|
86
|
+
const thinkingOptionId = resolveThinkingOptionId(model, candidate.thinkingOptionId);
|
|
87
|
+
return {
|
|
88
|
+
provider,
|
|
89
|
+
...(model ? { model: model.id } : {}),
|
|
90
|
+
...(thinkingOptionId ? { thinkingOptionId } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (!configuredModel) {
|
|
94
|
+
return {
|
|
95
|
+
provider,
|
|
96
|
+
...(candidate.thinkingOptionId ? { thinkingOptionId: candidate.thinkingOptionId } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const nestedMatch = resolveNestedProviderModel(provider, configuredModel, readyEntries);
|
|
100
|
+
if (!nestedMatch) {
|
|
101
|
+
return {
|
|
102
|
+
provider,
|
|
103
|
+
model: configuredModel,
|
|
104
|
+
...(candidate.thinkingOptionId ? { thinkingOptionId: candidate.thinkingOptionId } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const thinkingOptionId = resolveThinkingOptionId(nestedMatch.model, candidate.thinkingOptionId);
|
|
108
|
+
return {
|
|
109
|
+
provider: nestedMatch.provider,
|
|
110
|
+
model: nestedMatch.model.id,
|
|
111
|
+
...(thinkingOptionId ? { thinkingOptionId } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function resolveNestedProviderModel(providerId, modelId, entries) {
|
|
115
|
+
const normalizedProviderId = providerId.trim().toLowerCase();
|
|
116
|
+
const normalizedModelId = modelId.trim().toLowerCase();
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
for (const model of entry.models ?? []) {
|
|
119
|
+
const modelProviderId = readModelMetadataString(model, "providerId")?.toLowerCase();
|
|
120
|
+
const nestedModelId = readModelMetadataString(model, "modelId")?.toLowerCase();
|
|
121
|
+
if (modelProviderId !== normalizedProviderId) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (normalizedModelId === model.id.toLowerCase() ||
|
|
125
|
+
normalizedModelId === nestedModelId ||
|
|
126
|
+
model.id.toLowerCase() === `${normalizedProviderId}/${normalizedModelId}`) {
|
|
127
|
+
return { provider: entry.provider, model };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
function resolveByModelSubstring(entries, identifier) {
|
|
134
|
+
const needle = identifier.modelSubstring.trim().toLowerCase();
|
|
135
|
+
if (!needle) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
for (const model of entry.models ?? []) {
|
|
140
|
+
const haystacks = [model.id, model.label].map((value) => value.toLowerCase());
|
|
141
|
+
if (!haystacks.some((value) => value.includes(needle))) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const thinkingOptionId = resolveThinkingOptionId(model, identifier.thinkingOptionId);
|
|
145
|
+
return {
|
|
146
|
+
provider: entry.provider,
|
|
147
|
+
model: model.id,
|
|
148
|
+
...(thinkingOptionId ? { thinkingOptionId } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function readConfiguredProviders(daemonConfig) {
|
|
155
|
+
const metadataGeneration = daemonConfig?.metadataGeneration;
|
|
156
|
+
if (!metadataGeneration || typeof metadataGeneration !== "object") {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const providers = "providers" in metadataGeneration ? metadataGeneration.providers : undefined;
|
|
160
|
+
return Array.isArray(providers) ? providers : [];
|
|
161
|
+
}
|
|
162
|
+
function selectDefaultModel(models) {
|
|
163
|
+
return models.find((model) => model.isDefault) ?? models[0] ?? null;
|
|
164
|
+
}
|
|
165
|
+
function resolveThinkingOptionId(model, preferredThinkingOptionId) {
|
|
166
|
+
if (!model) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
if (preferredThinkingOptionId &&
|
|
170
|
+
model.thinkingOptions?.some((option) => option.id === preferredThinkingOptionId)) {
|
|
171
|
+
return preferredThinkingOptionId;
|
|
172
|
+
}
|
|
173
|
+
return model.defaultThinkingOptionId;
|
|
174
|
+
}
|
|
175
|
+
function dedupeProviders(providers) {
|
|
176
|
+
const seen = new Set();
|
|
177
|
+
const deduped = [];
|
|
178
|
+
for (const provider of providers) {
|
|
179
|
+
const key = [provider.provider, provider.model ?? "", provider.thinkingOptionId ?? ""].join("\0");
|
|
180
|
+
if (seen.has(key)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
seen.add(key);
|
|
184
|
+
deduped.push(provider);
|
|
185
|
+
}
|
|
186
|
+
return deduped;
|
|
187
|
+
}
|
|
188
|
+
function readModelMetadataString(model, key) {
|
|
189
|
+
const value = model.metadata?.[key];
|
|
190
|
+
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=structured-generation-providers.js.map
|
|
@@ -2,7 +2,6 @@ import type { Logger } from "pino";
|
|
|
2
2
|
import type { AgentManager } from "../agent/agent-manager.js";
|
|
3
3
|
import type { AgentStorage } from "../agent/agent-storage.js";
|
|
4
4
|
import type { DaemonConfigStore } from "../daemon-config-store.js";
|
|
5
|
-
import type { SessionOutboundMessage } from "../messages.js";
|
|
6
5
|
import { archivePaseoWorktree, killTerminalsUnderPath } from "../paseo-worktree-archive-service.js";
|
|
7
6
|
import { isSameOrDescendantPath } from "../path-utils.js";
|
|
8
7
|
import type { WorkspaceGitRuntimeSnapshot, WorkspaceGitServiceImpl } from "../workspace-git-service.js";
|
|
@@ -21,7 +20,6 @@ export interface AutoArchiveArchiveOptions {
|
|
|
21
20
|
markWorkspaceArchiving: (workspaceIds: Iterable<string>, archivingAt: string) => void;
|
|
22
21
|
clearWorkspaceArchiving: (workspaceIds: Iterable<string>) => void;
|
|
23
22
|
emitWorkspaceUpdatesForWorkspaceIds: (workspaceIds: Iterable<string>) => Promise<void>;
|
|
24
|
-
emitSessionMessage: (message: SessionOutboundMessage) => void;
|
|
25
23
|
}
|
|
26
24
|
export interface ArchiveIfSafeDependencies {
|
|
27
25
|
archivePaseoWorktree: typeof archivePaseoWorktree;
|
|
@@ -49,7 +49,6 @@ export async function archiveIfSafe(input) {
|
|
|
49
49
|
agentManager: options.agentManager,
|
|
50
50
|
agentStorage: options.agentStorage,
|
|
51
51
|
archiveWorkspaceRecord: options.archiveWorkspaceRecord,
|
|
52
|
-
emit: options.emitSessionMessage,
|
|
53
52
|
emitWorkspaceUpdatesForWorkspaceIds: options.emitWorkspaceUpdatesForWorkspaceIds,
|
|
54
53
|
markWorkspaceArchiving: options.markWorkspaceArchiving,
|
|
55
54
|
clearWorkspaceArchiving: options.clearWorkspaceArchiving,
|
|
@@ -76,6 +76,13 @@ export interface PaseoDaemonConfig {
|
|
|
76
76
|
dictationFinalTimeoutMs?: number;
|
|
77
77
|
downloadTokenTtlMs?: number;
|
|
78
78
|
agentProviderSettings?: AgentProviderRuntimeSettingsMap;
|
|
79
|
+
metadataGeneration?: {
|
|
80
|
+
providers?: Array<{
|
|
81
|
+
provider: string;
|
|
82
|
+
model?: string;
|
|
83
|
+
thinkingOptionId?: string;
|
|
84
|
+
}>;
|
|
85
|
+
};
|
|
79
86
|
providerOverrides?: Record<string, ProviderOverride>;
|
|
80
87
|
log?: PersistedConfig["log"];
|
|
81
88
|
onLifecycleIntent?: (intent: DaemonLifecycleIntent) => void;
|
|
@@ -169,6 +169,9 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
169
169
|
...(override.additionalModels ? { additionalModels: override.additionalModels } : {}),
|
|
170
170
|
},
|
|
171
171
|
])),
|
|
172
|
+
metadataGeneration: {
|
|
173
|
+
providers: config.metadataGeneration?.providers ?? [],
|
|
174
|
+
},
|
|
172
175
|
autoArchiveAfterMerge: config.autoArchiveAfterMerge ?? false,
|
|
173
176
|
appendSystemPrompt: config.appendSystemPrompt ?? "",
|
|
174
177
|
}, logger);
|
|
@@ -417,6 +420,14 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
417
420
|
agentStorage,
|
|
418
421
|
});
|
|
419
422
|
await scheduleService.start();
|
|
423
|
+
agentManager.setAgentArchivedCallback(async (agentId) => {
|
|
424
|
+
try {
|
|
425
|
+
await scheduleService.deleteForAgent(agentId);
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
logger.warn({ err: error, agentId }, "Failed to delete schedules for archived agent");
|
|
429
|
+
}
|
|
430
|
+
});
|
|
420
431
|
logger.info({ elapsed: elapsed() }, "Schedule service initialized");
|
|
421
432
|
logger.info({ elapsed: elapsed() }, "Loading persisted agent registry");
|
|
422
433
|
const persistedRecords = await agentStorage.list();
|
|
@@ -467,7 +478,6 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
467
478
|
markWorkspaceArchiving: markWorkspaceArchivingExternal,
|
|
468
479
|
clearWorkspaceArchiving: clearWorkspaceArchivingExternal,
|
|
469
480
|
emitWorkspaceUpdatesForWorkspaceIds: emitWorkspaceUpdatesExternal,
|
|
470
|
-
emitSessionMessage: emitExternalSessionMessage,
|
|
471
481
|
});
|
|
472
482
|
const mcpEnabled = config.mcpEnabled ?? true;
|
|
473
483
|
let agentMcpBaseUrl = null;
|
|
@@ -488,7 +498,6 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
488
498
|
emitWorkspaceUpdatesForWorkspaceIds: emitWorkspaceUpdatesExternal,
|
|
489
499
|
markWorkspaceArchiving: markWorkspaceArchivingExternal,
|
|
490
500
|
clearWorkspaceArchiving: clearWorkspaceArchivingExternal,
|
|
491
|
-
emitSessionMessage: emitExternalSessionMessage,
|
|
492
501
|
createPaseoWorktree: async (input, serviceOptions) => {
|
|
493
502
|
return createPaseoWorktreeWorkflow({
|
|
494
503
|
paseoHome: config.paseoHome,
|
|
@@ -207,6 +207,7 @@ export function loadConfig(paseoHome, options) {
|
|
|
207
207
|
voiceLlmProviderExplicit: voiceLlm.providerExplicit,
|
|
208
208
|
voiceLlmModel: voiceLlm.model,
|
|
209
209
|
agentProviderSettings: extractAgentProviderSettings(providerOverrides),
|
|
210
|
+
metadataGeneration: persisted.agents?.metadataGeneration,
|
|
210
211
|
providerOverrides,
|
|
211
212
|
log: resolveLogConfigFromEnv(env, persisted),
|
|
212
213
|
};
|
|
@@ -114,8 +114,29 @@ export class DaemonConfigStore {
|
|
|
114
114
|
}
|
|
115
115
|
function mergeMutableConfigIntoPersistedConfig(params) {
|
|
116
116
|
const { persisted, mutable } = params;
|
|
117
|
+
const metadataGenerationProviders = readMetadataGenerationProviders(mutable);
|
|
117
118
|
const providerOverrides = applyMutableProviderConfigToOverrides(persisted.agents?.providers, mutable.providers);
|
|
118
119
|
const persistedAgents = persisted.agents;
|
|
120
|
+
const persistedMetadataGeneration = {
|
|
121
|
+
providers: metadataGenerationProviders,
|
|
122
|
+
};
|
|
123
|
+
const shouldPersistMetadataGeneration = metadataGenerationProviders.length > 0 || persisted.agents?.metadataGeneration !== undefined;
|
|
124
|
+
let nextAgents = persisted.agents;
|
|
125
|
+
if (providerOverrides && Object.keys(providerOverrides).length > 0) {
|
|
126
|
+
nextAgents = {
|
|
127
|
+
...persistedAgents,
|
|
128
|
+
providers: providerOverrides,
|
|
129
|
+
...(shouldPersistMetadataGeneration
|
|
130
|
+
? { metadataGeneration: persistedMetadataGeneration }
|
|
131
|
+
: {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
else if (shouldPersistMetadataGeneration) {
|
|
135
|
+
nextAgents = {
|
|
136
|
+
...persistedAgents,
|
|
137
|
+
metadataGeneration: persistedMetadataGeneration,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
119
140
|
return {
|
|
120
141
|
...persisted,
|
|
121
142
|
daemon: {
|
|
@@ -127,12 +148,31 @@ function mergeMutableConfigIntoPersistedConfig(params) {
|
|
|
127
148
|
autoArchiveAfterMerge: mutable.autoArchiveAfterMerge,
|
|
128
149
|
appendSystemPrompt: mutable.appendSystemPrompt,
|
|
129
150
|
},
|
|
130
|
-
agents:
|
|
131
|
-
? {
|
|
132
|
-
...persistedAgents,
|
|
133
|
-
providers: providerOverrides,
|
|
134
|
-
}
|
|
135
|
-
: persisted.agents,
|
|
151
|
+
agents: nextAgents,
|
|
136
152
|
};
|
|
137
153
|
}
|
|
154
|
+
function readMetadataGenerationProviders(mutable) {
|
|
155
|
+
const metadataGeneration = mutable.metadataGeneration;
|
|
156
|
+
if (!isRecord(metadataGeneration)) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const providers = metadataGeneration["providers"];
|
|
160
|
+
if (!Array.isArray(providers)) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
return providers.flatMap((entry) => {
|
|
164
|
+
if (!isRecord(entry) || typeof entry["provider"] !== "string") {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
return [
|
|
168
|
+
{
|
|
169
|
+
provider: entry["provider"],
|
|
170
|
+
...(typeof entry["model"] === "string" ? { model: entry["model"] } : {}),
|
|
171
|
+
...(typeof entry["thinkingOptionId"] === "string"
|
|
172
|
+
? { thinkingOptionId: entry["thinkingOptionId"] }
|
|
173
|
+
: {}),
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
});
|
|
177
|
+
}
|
|
138
178
|
//# sourceMappingURL=daemon-config-store.js.map
|
|
@@ -2,6 +2,7 @@ import { createPaseoDaemon } from "./bootstrap.js";
|
|
|
2
2
|
import { loadConfig } from "./config.js";
|
|
3
3
|
import { resolvePaseoHome } from "./paseo-home.js";
|
|
4
4
|
import { createRootLogger } from "./logger.js";
|
|
5
|
+
process.title = "Paseo Daemon";
|
|
5
6
|
function bootstrapFromEnvironment() {
|
|
6
7
|
try {
|
|
7
8
|
const paseoHome = resolvePaseoHome();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { constants, promises as fs } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { resolvePathFromBase } from "../path-utils.js";
|
|
3
|
+
import { expandUserPath, resolvePathFromBase } from "../path-utils.js";
|
|
4
4
|
const TEXT_MIME_TYPES = {
|
|
5
5
|
".json": "application/json",
|
|
6
6
|
};
|
|
@@ -168,7 +168,7 @@ export async function getDownloadableFileInfo({ root, relativePath }) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
async function resolveScopedPath({ root, relativePath = ".", }) {
|
|
171
|
-
const normalizedRoot =
|
|
171
|
+
const normalizedRoot = expandUserPath(root);
|
|
172
172
|
const requestedPath = resolvePathFromBase(normalizedRoot, relativePath);
|
|
173
173
|
const relative = path.relative(normalizedRoot, requestedPath);
|
|
174
174
|
if (relative !== "" && (relative.startsWith("..") || path.isAbsolute(relative))) {
|
|
@@ -215,8 +215,8 @@ function isOutsideWorkspaceError(error) {
|
|
|
215
215
|
return error instanceof Error && error.message === ACCESS_OUTSIDE_WORKSPACE_MESSAGE;
|
|
216
216
|
}
|
|
217
217
|
function normalizeRelativePath({ root, targetPath }) {
|
|
218
|
-
const normalizedRoot =
|
|
219
|
-
const normalizedTarget =
|
|
218
|
+
const normalizedRoot = expandUserPath(root);
|
|
219
|
+
const normalizedTarget = expandUserPath(targetPath);
|
|
220
220
|
const relative = path.relative(normalizedRoot, normalizedTarget);
|
|
221
221
|
return relative === "" ? "." : relative.split(path.sep).join("/");
|
|
222
222
|
}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import type { Logger } from "pino";
|
|
2
2
|
import type { AgentManager } from "./agent/agent-manager.js";
|
|
3
3
|
import type { AgentStorage } from "./agent/agent-storage.js";
|
|
4
|
-
import type { SessionOutboundMessage } from "./messages.js";
|
|
5
4
|
import type { WorkspaceGitService } from "./workspace-git-service.js";
|
|
6
5
|
import type { GitHubService } from "../services/github-service.js";
|
|
7
6
|
import type { TerminalManager } from "../terminal/terminal-manager.js";
|
|
8
|
-
type EmitSessionMessage = (message: SessionOutboundMessage) => void;
|
|
9
7
|
export interface ArchivePaseoWorktreeDependencies {
|
|
10
8
|
paseoHome?: string;
|
|
11
9
|
github: GitHubService;
|
|
12
10
|
workspaceGitService: Pick<WorkspaceGitService, "getSnapshot">;
|
|
13
|
-
agentManager: Pick<AgentManager, "listAgents" | "
|
|
14
|
-
agentStorage: Pick<AgentStorage, "list"
|
|
11
|
+
agentManager: Pick<AgentManager, "listAgents" | "archiveAgent" | "archiveSnapshot">;
|
|
12
|
+
agentStorage: Pick<AgentStorage, "list">;
|
|
15
13
|
archiveWorkspaceRecord: (workspaceId: string) => Promise<void>;
|
|
16
|
-
emit: EmitSessionMessage;
|
|
17
14
|
emitWorkspaceUpdatesForWorkspaceIds: (workspaceIds: Iterable<string>) => Promise<void>;
|
|
18
15
|
markWorkspaceArchiving: (workspaceIds: Iterable<string>, archivingAt: string) => void;
|
|
19
16
|
clearWorkspaceArchiving: (workspaceIds: Iterable<string>) => void;
|
|
@@ -39,5 +36,4 @@ export declare function archivePaseoWorktree(dependencies: ArchivePaseoWorktreeD
|
|
|
39
36
|
requestId: string;
|
|
40
37
|
}): Promise<string[]>;
|
|
41
38
|
export declare function killTerminalsUnderPath(dependencies: KillTerminalsUnderPathDependencies, rootPath: string): Promise<void>;
|
|
42
|
-
export {};
|
|
43
39
|
//# sourceMappingURL=paseo-worktree-archive-service.d.ts.map
|
|
@@ -8,14 +8,14 @@ export async function archivePaseoWorktree(dependencies, options) {
|
|
|
8
8
|
if (resolvedWorktree) {
|
|
9
9
|
targetPath = resolvedWorktree.worktreePath;
|
|
10
10
|
}
|
|
11
|
-
const
|
|
11
|
+
const archivedAgents = new Set();
|
|
12
12
|
const affectedWorkspaceCwds = new Set([targetPath]);
|
|
13
13
|
const affectedWorkspaceIds = new Set([normalizePersistedWorkspaceId(targetPath)]);
|
|
14
14
|
const liveAgents = dependencies.agentManager
|
|
15
15
|
.listAgents()
|
|
16
16
|
.filter((agent) => dependencies.isPathWithinRoot(targetPath, agent.cwd));
|
|
17
17
|
for (const agent of liveAgents) {
|
|
18
|
-
|
|
18
|
+
archivedAgents.add(agent.id);
|
|
19
19
|
affectedWorkspaceCwds.add(agent.cwd);
|
|
20
20
|
affectedWorkspaceIds.add(normalizePersistedWorkspaceId(agent.cwd));
|
|
21
21
|
}
|
|
@@ -26,41 +26,30 @@ export async function archivePaseoWorktree(dependencies, options) {
|
|
|
26
26
|
catch (error) {
|
|
27
27
|
dependencies.sessionLogger?.warn({ err: error, targetPath }, "Failed to list stored agents during worktree archive; continuing");
|
|
28
28
|
}
|
|
29
|
+
const liveAgentIds = new Set(liveAgents.map((agent) => agent.id));
|
|
29
30
|
const matchingStoredRecords = storedRecords.filter((record) => dependencies.isPathWithinRoot(targetPath, record.cwd));
|
|
30
31
|
for (const record of matchingStoredRecords) {
|
|
31
|
-
|
|
32
|
+
archivedAgents.add(record.id);
|
|
32
33
|
affectedWorkspaceCwds.add(record.cwd);
|
|
33
34
|
affectedWorkspaceIds.add(normalizePersistedWorkspaceId(record.cwd));
|
|
34
35
|
}
|
|
35
|
-
const agentIdsToRemoveFromStorage = new Set([
|
|
36
|
-
...liveAgents.map((agent) => agent.id),
|
|
37
|
-
...matchingStoredRecords.map((record) => record.id),
|
|
38
|
-
]);
|
|
39
36
|
const affectedWorkspaceIdList = Array.from(affectedWorkspaceIds);
|
|
40
37
|
dependencies.markWorkspaceArchiving(affectedWorkspaceIdList, new Date().toISOString());
|
|
41
38
|
try {
|
|
42
39
|
await dependencies.emitWorkspaceUpdatesForWorkspaceIds(affectedWorkspaceIdList);
|
|
43
|
-
const
|
|
44
|
-
|
|
40
|
+
const archivedAt = new Date().toISOString();
|
|
41
|
+
const archiveResults = await Promise.allSettled([
|
|
42
|
+
...liveAgents.map((agent) => dependencies.agentManager.archiveAgent(agent.id)),
|
|
43
|
+
...matchingStoredRecords
|
|
44
|
+
.filter((record) => !liveAgentIds.has(record.id) && !record.archivedAt)
|
|
45
|
+
.map((record) => dependencies.agentManager.archiveSnapshot(record.id, archivedAt)),
|
|
45
46
|
dependencies.killTerminalsUnderPath(targetPath),
|
|
46
47
|
]);
|
|
47
|
-
for (const result of
|
|
48
|
+
for (const result of archiveResults) {
|
|
48
49
|
if (result.status === "rejected") {
|
|
49
|
-
dependencies.sessionLogger?.warn({ err: result.reason, targetPath }, "Worktree teardown step failed
|
|
50
|
+
dependencies.sessionLogger?.warn({ err: result.reason, targetPath }, "Worktree archive teardown step failed; continuing");
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
|
-
const agentIdsToRemove = Array.from(agentIdsToRemoveFromStorage);
|
|
53
|
-
const storageRemovalResults = await Promise.allSettled(agentIdsToRemove.map((agentId) => dependencies.agentStorage.remove(agentId)));
|
|
54
|
-
storageRemovalResults.forEach((result, index) => {
|
|
55
|
-
if (result.status === "fulfilled") {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
dependencies.sessionLogger?.warn({
|
|
59
|
-
err: result.reason,
|
|
60
|
-
agentId: agentIdsToRemove[index],
|
|
61
|
-
targetPath,
|
|
62
|
-
}, "Failed to remove archived worktree agent from storage; continuing");
|
|
63
|
-
});
|
|
64
53
|
await deletePaseoWorktree({
|
|
65
54
|
cwd: options.repoRoot,
|
|
66
55
|
worktreePath: targetPath,
|
|
@@ -89,21 +78,12 @@ export async function archivePaseoWorktree(dependencies, options) {
|
|
|
89
78
|
dependencies.sessionLogger?.warn({ err: error, workspaceId }, "Failed to archive workspace record; worktree FS already removed");
|
|
90
79
|
}
|
|
91
80
|
}));
|
|
92
|
-
for (const agentId of removedAgents) {
|
|
93
|
-
dependencies.emit({
|
|
94
|
-
type: "agent_deleted",
|
|
95
|
-
payload: {
|
|
96
|
-
agentId,
|
|
97
|
-
requestId: options.requestId,
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
81
|
}
|
|
102
82
|
finally {
|
|
103
83
|
dependencies.clearWorkspaceArchiving(affectedWorkspaceIdList);
|
|
104
84
|
await dependencies.emitWorkspaceUpdatesForWorkspaceIds(affectedWorkspaceIdList);
|
|
105
85
|
}
|
|
106
|
-
return Array.from(
|
|
86
|
+
return Array.from(archivedAgents);
|
|
107
87
|
}
|
|
108
88
|
export async function killTerminalsUnderPath(dependencies, rootPath) {
|
|
109
89
|
const terminalManager = dependencies.terminalManager;
|