@getpaseo/server 0.1.97-beta.3 → 0.1.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/server/agent/agent-manager.js +1 -1
- package/dist/server/server/agent/agent-response-loop.js +9 -3
- package/dist/server/server/agent/agent-storage.d.ts +20 -240
- package/dist/server/server/agent/agent-storage.js +6 -6
- package/dist/server/server/agent/mcp-server.js +9 -4
- package/dist/server/server/agent/mcp-shared.d.ts +35 -179
- package/dist/server/server/agent/providers/claude/project-dir.js +9 -6
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -22
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +8 -118
- package/dist/server/server/agent/providers/generic-acp-agent.d.ts +1 -5
- package/dist/server/server/agent/providers/pi/agent.d.ts +1 -5
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +391 -1261
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +26 -16
- package/dist/server/server/loop-service.d.ts +60 -359
- package/dist/server/server/migrations/backfill-workspace-id.migration.js +10 -6
- package/dist/server/server/package-version.d.ts +1 -7
- package/dist/server/server/paseo-worktree-service.js +15 -1
- package/dist/server/server/persisted-config.d.ts +138 -1009
- package/dist/server/server/persisted-config.js +1 -1
- package/dist/server/server/pid-lock.d.ts +1 -15
- package/dist/server/server/session.d.ts +6 -0
- package/dist/server/server/session.js +195 -25
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +2 -2
- package/dist/server/server/speech/speech-types.d.ts +9 -11
- package/dist/server/server/websocket-server.js +4 -0
- package/dist/server/server/workspace-registry.d.ts +17 -48
- package/dist/server/server/workspace-registry.js +9 -0
- package/dist/server/terminal/terminal-session-controller.d.ts +8 -0
- package/dist/server/terminal/terminal-session-controller.js +23 -3
- package/dist/server/utils/checkout-git.js +36 -76
- package/dist/server/utils/worktree-metadata.d.ts +7 -59
- package/dist/src/server/persisted-config.js +1 -1
- package/package.json +9 -9
|
@@ -213,7 +213,7 @@ export const PersistedConfigSchema = z
|
|
|
213
213
|
// localhost service proxying remains always enabled.
|
|
214
214
|
enabled: z.boolean().optional(),
|
|
215
215
|
listen: z.string().optional(),
|
|
216
|
-
publicBaseUrl: z.
|
|
216
|
+
publicBaseUrl: z.url().optional(),
|
|
217
217
|
})
|
|
218
218
|
.strict()
|
|
219
219
|
.optional(),
|
|
@@ -6,21 +6,7 @@ export declare const pidLockInfoSchema: z.ZodObject<{
|
|
|
6
6
|
uid: z.ZodNumber;
|
|
7
7
|
listen: z.ZodNullable<z.ZodString>;
|
|
8
8
|
desktopManaged: z.ZodOptional<z.ZodBoolean>;
|
|
9
|
-
},
|
|
10
|
-
hostname: string;
|
|
11
|
-
uid: number;
|
|
12
|
-
startedAt: string;
|
|
13
|
-
listen: string | null;
|
|
14
|
-
pid: number;
|
|
15
|
-
desktopManaged?: boolean | undefined;
|
|
16
|
-
}, {
|
|
17
|
-
hostname: string;
|
|
18
|
-
uid: number;
|
|
19
|
-
startedAt: string;
|
|
20
|
-
listen: string | null;
|
|
21
|
-
pid: number;
|
|
22
|
-
desktopManaged?: boolean | undefined;
|
|
23
|
-
}>;
|
|
9
|
+
}, z.core.$strip>;
|
|
24
10
|
export interface PidLockInfo extends z.infer<typeof pidLockInfoSchema> {
|
|
25
11
|
}
|
|
26
12
|
export declare class PidLockError extends Error {
|
|
@@ -278,6 +278,7 @@ export declare class Session {
|
|
|
278
278
|
private resolveWorkspaceDirectory;
|
|
279
279
|
private buildProjectPlacementForWorkspace;
|
|
280
280
|
private buildProjectPlacementForWorkspaceId;
|
|
281
|
+
private buildProjectPlacementForExistingWorkspaceProject;
|
|
281
282
|
private forwardAgentUpdate;
|
|
282
283
|
/**
|
|
283
284
|
* Main entry point for processing session messages
|
|
@@ -312,6 +313,7 @@ export declare class Session {
|
|
|
312
313
|
private unarchiveAgentByHandle;
|
|
313
314
|
private handleUpdateAgentRequest;
|
|
314
315
|
private handleProjectRenameRequest;
|
|
316
|
+
private handleProjectRemoveRequest;
|
|
315
317
|
private handleWorkspaceTitleSetRequest;
|
|
316
318
|
private toVoiceFeatureUnavailableContext;
|
|
317
319
|
private resolveModeReadinessState;
|
|
@@ -484,6 +486,8 @@ export declare class Session {
|
|
|
484
486
|
private createWorkspaceForDirectory;
|
|
485
487
|
private reclassifyOrUnarchiveWorkspaceForDirectory;
|
|
486
488
|
private resolveProjectRecordForPlacement;
|
|
489
|
+
private unarchiveOwningWorkspaceForAgent;
|
|
490
|
+
private recreateOwningWorktreeForRestore;
|
|
487
491
|
private ensureWorkspaceRecordUnarchived;
|
|
488
492
|
private createPaseoWorktree;
|
|
489
493
|
private listActiveWorkspaceRefs;
|
|
@@ -492,6 +496,8 @@ export declare class Session {
|
|
|
492
496
|
private reconcileAndEmitWorkspaceUpdates;
|
|
493
497
|
private reconcileActiveWorkspaceRecords;
|
|
494
498
|
private emitWorkspaceUpdatesForWorkspaceIds;
|
|
499
|
+
private recordWorkspaceGitDescriptorState;
|
|
500
|
+
private buildWorkspaceRemoveUpdatePayload;
|
|
495
501
|
private resolveEmptyProjectForArchivedWorkspace;
|
|
496
502
|
private emitWorkspaceUpdateForTerminalContribution;
|
|
497
503
|
private emitWorkspaceUpdateForCwd;
|
|
@@ -2,7 +2,7 @@ import equal from "fast-deep-equal";
|
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { stat } from "node:fs/promises";
|
|
5
|
-
import { resolve, sep } from "path";
|
|
5
|
+
import { basename, normalize, resolve, sep } from "path";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { CLIENT_CAPS } from "@getpaseo/protocol/client-capabilities";
|
|
@@ -50,7 +50,7 @@ import { isVoicePermissionAllowed } from "./voice-permission-policy.js";
|
|
|
50
50
|
import { listDirectoryEntries, readExplorerFile, readExplorerFileBytes, getDownloadableFileInfo, } from "./file-explorer/service.js";
|
|
51
51
|
import { readPaseoConfigForEdit, writePaseoConfigForEdit, } from "../utils/paseo-config-file.js";
|
|
52
52
|
import { buildMetadataPrompt } from "../utils/build-metadata-prompt.js";
|
|
53
|
-
import { archivePersistedWorkspaceRecord } from "./workspace-archive-service.js";
|
|
53
|
+
import { archivePersistedWorkspaceRecord, archiveWorkspaceContents, } from "./workspace-archive-service.js";
|
|
54
54
|
import { WorkspaceReconciliationService } from "./workspace-reconciliation-service.js";
|
|
55
55
|
import { checkoutResolvedBranch, commitChanges, mergeToBase, mergeFromBase, pullCurrentBranch, pushCurrentBranch, createPullRequest, renameCurrentBranch as renameCurrentBranchDefault, } from "../utils/checkout-git.js";
|
|
56
56
|
import { validateBranchSlug } from "@getpaseo/protocol/branch-slug";
|
|
@@ -70,7 +70,9 @@ import { attemptFirstAgentBranchAutoName, createLocalCheckoutWorkspace, createPa
|
|
|
70
70
|
import { generateBranchNameFromFirstAgentContext, } from "./worktree-branch-name-generator.js";
|
|
71
71
|
import { assertSafeGitRef as assertWorktreeSafeGitRef, buildAgentSessionConfig as buildWorktreeAgentSessionConfig, createPaseoWorktreeWorkflow as createWorktreeWorkflow, handleCreatePaseoWorktreeRequest as handleCreateWorktreeRequest, handlePaseoWorktreeArchiveRequest as handleWorktreeArchiveRequest, handlePaseoWorktreeListRequest as handleWorktreeListRequest, handleWorkspaceSetupStatusRequest as handleWorkspaceSetupStatusRequestMessage, } from "./worktree-session.js";
|
|
72
72
|
import { archiveByScope } from "./workspace-archive-service.js";
|
|
73
|
-
import { toWorktreeWireError } from "./worktree-errors.js";
|
|
73
|
+
import { WorktreeRequestError, toWorktreeRequestError, toWorktreeWireError, } from "./worktree-errors.js";
|
|
74
|
+
import { createWorktree } from "../utils/worktree.js";
|
|
75
|
+
import { runGitCommand } from "../utils/run-git-command.js";
|
|
74
76
|
import { CreateAgentLifecycleDispatch } from "./agent/create-agent-lifecycle-dispatch.js";
|
|
75
77
|
const WORKSPACE_GIT_WATCH_REMOVED_STATE_KEY = "__removed__";
|
|
76
78
|
async function resolveKnownProjectRootForConfig(input) {
|
|
@@ -223,7 +225,7 @@ const PCM_BITS_PER_SAMPLE = 16;
|
|
|
223
225
|
const PCM_BYTES_PER_MS = (PCM_SAMPLE_RATE * PCM_CHANNELS * (PCM_BITS_PER_SAMPLE / 8)) / 1000;
|
|
224
226
|
const MIN_STREAMING_SEGMENT_DURATION_MS = 1000;
|
|
225
227
|
const MIN_STREAMING_SEGMENT_BYTES = Math.round(PCM_BYTES_PER_MS * MIN_STREAMING_SEGMENT_DURATION_MS);
|
|
226
|
-
const AgentIdSchema = z.
|
|
228
|
+
const AgentIdSchema = z.guid();
|
|
227
229
|
const nodeSessionFileSystem = {
|
|
228
230
|
async isDirectory(path) {
|
|
229
231
|
const stats = await stat(path).catch(() => null);
|
|
@@ -382,12 +384,7 @@ export class Session {
|
|
|
382
384
|
hasBinaryChannel: () => this.onBinaryMessage !== null,
|
|
383
385
|
isPathWithinRoot: (rootPath, candidatePath) => this.isPathWithinRoot(rootPath, candidatePath),
|
|
384
386
|
sessionLogger: this.sessionLogger,
|
|
385
|
-
|
|
386
|
-
const workspaces = await this.workspaceRegistry.list();
|
|
387
|
-
return workspaces
|
|
388
|
-
.filter((workspace) => !workspace.archivedAt)
|
|
389
|
-
.map((workspace) => workspace.cwd);
|
|
390
|
-
},
|
|
387
|
+
listTerminalWorkspaceRefs: () => this.listActiveWorkspaceRefs(),
|
|
391
388
|
clientSupportsWrapReflow: () => this.clientCapabilities.has(CLIENT_CAPS.terminalReflowableSnapshot),
|
|
392
389
|
getClientBufferedAmount: () => this.getTransportBufferedAmount(),
|
|
393
390
|
});
|
|
@@ -911,6 +908,7 @@ export class Session {
|
|
|
911
908
|
return {
|
|
912
909
|
projectKey: project.projectId,
|
|
913
910
|
projectName: resolveProjectDisplayName(project),
|
|
911
|
+
workspaceName: resolveWorkspaceDisplayName(workspace),
|
|
914
912
|
checkout,
|
|
915
913
|
};
|
|
916
914
|
}
|
|
@@ -920,6 +918,15 @@ export class Session {
|
|
|
920
918
|
return null;
|
|
921
919
|
return this.buildProjectPlacementForWorkspace(workspace);
|
|
922
920
|
}
|
|
921
|
+
async buildProjectPlacementForExistingWorkspaceProject(workspaceId) {
|
|
922
|
+
const workspace = await this.workspaceRegistry.get(workspaceId);
|
|
923
|
+
if (!workspace)
|
|
924
|
+
return null;
|
|
925
|
+
const project = await this.projectRegistry.get(workspace.projectId);
|
|
926
|
+
if (!project)
|
|
927
|
+
return null;
|
|
928
|
+
return this.buildProjectPlacementForWorkspace(workspace, project);
|
|
929
|
+
}
|
|
923
930
|
async forwardAgentUpdate(agent) {
|
|
924
931
|
try {
|
|
925
932
|
const subscription = this.agentUpdatesSubscription;
|
|
@@ -1345,6 +1352,8 @@ export class Session {
|
|
|
1345
1352
|
return this.handleOpenProjectRequest(msg);
|
|
1346
1353
|
case "archive_workspace_request":
|
|
1347
1354
|
return this.handleArchiveWorkspaceRequest(msg);
|
|
1355
|
+
case "project.remove.request":
|
|
1356
|
+
return this.handleProjectRemoveRequest(msg);
|
|
1348
1357
|
case "workspace.create.request":
|
|
1349
1358
|
return this.handleWorkspaceCreateRequest(msg);
|
|
1350
1359
|
case "workspace.clear_attention.request":
|
|
@@ -1764,6 +1773,80 @@ export class Session {
|
|
|
1764
1773
|
});
|
|
1765
1774
|
}
|
|
1766
1775
|
}
|
|
1776
|
+
async handleProjectRemoveRequest(request) {
|
|
1777
|
+
const { projectId, requestId } = request;
|
|
1778
|
+
this.sessionLogger.info({ projectId, requestId }, "session: project.remove.request");
|
|
1779
|
+
try {
|
|
1780
|
+
const projectWorkspaces = (await this.workspaceRegistry.list()).filter((workspace) => workspace.projectId === projectId);
|
|
1781
|
+
const activeWorkspaceIds = projectWorkspaces
|
|
1782
|
+
.filter((workspace) => !workspace.archivedAt)
|
|
1783
|
+
.map((workspace) => workspace.workspaceId);
|
|
1784
|
+
if (activeWorkspaceIds.length > 0) {
|
|
1785
|
+
this.markWorkspaceArchiving(activeWorkspaceIds, new Date().toISOString());
|
|
1786
|
+
await this.emitWorkspaceUpdatesForWorkspaceIds(activeWorkspaceIds, {
|
|
1787
|
+
skipReconcile: true,
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
const removedWorkspaceIds = [];
|
|
1791
|
+
try {
|
|
1792
|
+
for (const workspaceId of activeWorkspaceIds) {
|
|
1793
|
+
await archiveWorkspaceContents({
|
|
1794
|
+
agentManager: this.agentManager,
|
|
1795
|
+
agentStorage: this.agentStorage,
|
|
1796
|
+
killTerminalsForWorkspace: (id) => this.terminalController.killTerminalsForWorkspace(id),
|
|
1797
|
+
sessionLogger: this.sessionLogger,
|
|
1798
|
+
}, workspaceId);
|
|
1799
|
+
await this.archiveWorkspaceRecord(workspaceId);
|
|
1800
|
+
removedWorkspaceIds.push(workspaceId);
|
|
1801
|
+
}
|
|
1802
|
+
await this.projectRegistry.remove(projectId);
|
|
1803
|
+
}
|
|
1804
|
+
finally {
|
|
1805
|
+
if (activeWorkspaceIds.length > 0) {
|
|
1806
|
+
this.clearWorkspaceArchiving(activeWorkspaceIds);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const updateIds = removedWorkspaceIds.length > 0
|
|
1810
|
+
? removedWorkspaceIds
|
|
1811
|
+
: [projectWorkspaces[0]?.workspaceId ?? projectId];
|
|
1812
|
+
await this.emitWorkspaceUpdatesForWorkspaceIds(updateIds, {
|
|
1813
|
+
skipReconcile: true,
|
|
1814
|
+
removedProjectId: projectId,
|
|
1815
|
+
});
|
|
1816
|
+
this.emit({
|
|
1817
|
+
type: "project.remove.response",
|
|
1818
|
+
payload: {
|
|
1819
|
+
requestId,
|
|
1820
|
+
projectId,
|
|
1821
|
+
accepted: true,
|
|
1822
|
+
removedWorkspaceIds,
|
|
1823
|
+
error: null,
|
|
1824
|
+
},
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
catch (error) {
|
|
1828
|
+
this.sessionLogger.error({ err: error, projectId, requestId }, "session: project.remove.request error");
|
|
1829
|
+
this.emit({
|
|
1830
|
+
type: "activity_log",
|
|
1831
|
+
payload: {
|
|
1832
|
+
id: uuidv4(),
|
|
1833
|
+
timestamp: new Date(),
|
|
1834
|
+
type: "error",
|
|
1835
|
+
content: `Failed to remove project: ${getErrorMessage(error)}`,
|
|
1836
|
+
},
|
|
1837
|
+
});
|
|
1838
|
+
this.emit({
|
|
1839
|
+
type: "project.remove.response",
|
|
1840
|
+
payload: {
|
|
1841
|
+
requestId,
|
|
1842
|
+
projectId,
|
|
1843
|
+
accepted: false,
|
|
1844
|
+
removedWorkspaceIds: [],
|
|
1845
|
+
error: getErrorMessageOr(error, "Failed to remove project"),
|
|
1846
|
+
},
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1767
1850
|
async handleWorkspaceTitleSetRequest(workspaceId, title, requestId) {
|
|
1768
1851
|
this.sessionLogger.info({ workspaceId, requestId, hasTitle: typeof title === "string" }, "session: workspace.title.set.request");
|
|
1769
1852
|
try {
|
|
@@ -2412,6 +2495,7 @@ export class Session {
|
|
|
2412
2495
|
this.sessionLogger.info({ agentId }, `Refreshing agent ${agentId} from persistence`);
|
|
2413
2496
|
try {
|
|
2414
2497
|
await unarchiveAgentState(this.agentStorage, this.agentManager, agentId);
|
|
2498
|
+
await this.unarchiveOwningWorkspaceForAgent(agentId);
|
|
2415
2499
|
let snapshot;
|
|
2416
2500
|
const existing = this.agentManager.getAgent(agentId);
|
|
2417
2501
|
if (existing) {
|
|
@@ -2460,7 +2544,7 @@ export class Session {
|
|
|
2460
2544
|
requestId,
|
|
2461
2545
|
requestType: msg.type,
|
|
2462
2546
|
error: message,
|
|
2463
|
-
code: "agent_refresh_failed",
|
|
2547
|
+
code: error instanceof WorktreeRequestError ? error.code : "agent_refresh_failed",
|
|
2464
2548
|
},
|
|
2465
2549
|
});
|
|
2466
2550
|
}
|
|
@@ -4962,7 +5046,9 @@ export class Session {
|
|
|
4962
5046
|
if (existing) {
|
|
4963
5047
|
return existing;
|
|
4964
5048
|
}
|
|
4965
|
-
const placementPromise =
|
|
5049
|
+
const placementPromise = request.type === "fetch_agent_history_request"
|
|
5050
|
+
? this.buildProjectPlacementForExistingWorkspaceProject(workspaceId)
|
|
5051
|
+
: this.buildProjectPlacementForWorkspaceId(workspaceId);
|
|
4966
5052
|
placementByWorkspaceId.set(workspaceId, placementPromise);
|
|
4967
5053
|
return placementPromise;
|
|
4968
5054
|
};
|
|
@@ -5299,6 +5385,9 @@ export class Session {
|
|
|
5299
5385
|
if (input.workspace.projectId === projectId &&
|
|
5300
5386
|
input.workspace.kind === kind &&
|
|
5301
5387
|
input.workspace.displayName === displayName) {
|
|
5388
|
+
if (!input.project) {
|
|
5389
|
+
await this.projectRegistry.upsert(projectRecord);
|
|
5390
|
+
}
|
|
5302
5391
|
return this.ensureWorkspaceRecordUnarchived(input.workspace);
|
|
5303
5392
|
}
|
|
5304
5393
|
await this.projectRegistry.upsert(projectRecord);
|
|
@@ -5340,6 +5429,78 @@ export class Session {
|
|
|
5340
5429
|
updatedAt: input.timestamp,
|
|
5341
5430
|
};
|
|
5342
5431
|
}
|
|
5432
|
+
async unarchiveOwningWorkspaceForAgent(agentId) {
|
|
5433
|
+
const record = await this.agentStorage.get(agentId);
|
|
5434
|
+
if (!record?.workspaceId) {
|
|
5435
|
+
return;
|
|
5436
|
+
}
|
|
5437
|
+
const workspace = await this.workspaceRegistry.get(record.workspaceId);
|
|
5438
|
+
if (!workspace?.archivedAt) {
|
|
5439
|
+
return;
|
|
5440
|
+
}
|
|
5441
|
+
const directoryExists = await this.filesystem.isDirectory(record.cwd).catch(() => false);
|
|
5442
|
+
if (!directoryExists) {
|
|
5443
|
+
if (workspace.kind !== "worktree" || !workspace.branch) {
|
|
5444
|
+
return;
|
|
5445
|
+
}
|
|
5446
|
+
// Recreate the worktree directory from its kept branch BEFORE clearing
|
|
5447
|
+
// archivedAt — the reconciler re-archives workspaces whose directory is
|
|
5448
|
+
// missing, so the record must point at a real directory first.
|
|
5449
|
+
await this.recreateOwningWorktreeForRestore(workspace, workspace.branch);
|
|
5450
|
+
}
|
|
5451
|
+
await this.ensureWorkspaceRecordUnarchived(workspace);
|
|
5452
|
+
await this.emitWorkspaceUpdatesForWorkspaceIds([workspace.workspaceId]);
|
|
5453
|
+
}
|
|
5454
|
+
async recreateOwningWorktreeForRestore(workspace, branch) {
|
|
5455
|
+
const project = await this.projectRegistry.get(workspace.projectId);
|
|
5456
|
+
if (!project) {
|
|
5457
|
+
throw new WorktreeRequestError({
|
|
5458
|
+
code: "unknown",
|
|
5459
|
+
message: `Project ${workspace.projectId} not found for workspace ${workspace.workspaceId}`,
|
|
5460
|
+
});
|
|
5461
|
+
}
|
|
5462
|
+
const projectRootExists = await this.filesystem
|
|
5463
|
+
.isDirectory(project.rootPath)
|
|
5464
|
+
.catch(() => false);
|
|
5465
|
+
if (!projectRootExists) {
|
|
5466
|
+
throw new WorktreeRequestError({
|
|
5467
|
+
code: "unknown",
|
|
5468
|
+
message: `Project root is missing for ${workspace.projectId}: ${project.rootPath}`,
|
|
5469
|
+
});
|
|
5470
|
+
}
|
|
5471
|
+
// Archiving through the default path (scope "workspace", worktreePath only)
|
|
5472
|
+
// resolves repoRoot=null, so deletePaseoWorktree's `git worktree remove`/
|
|
5473
|
+
// `prune` is skipped and the admin registration survives — pinning the
|
|
5474
|
+
// branch as "already checked out". Prune here frees any stale registration
|
|
5475
|
+
// whose working dir is missing (a no-op for live worktrees) so the recreate
|
|
5476
|
+
// below succeeds regardless of how the worktree was archived.
|
|
5477
|
+
try {
|
|
5478
|
+
await runGitCommand(["worktree", "prune"], { cwd: project.rootPath, timeout: 30000 });
|
|
5479
|
+
}
|
|
5480
|
+
catch {
|
|
5481
|
+
// not critical; git will prune lazily
|
|
5482
|
+
}
|
|
5483
|
+
let result;
|
|
5484
|
+
try {
|
|
5485
|
+
result = await createWorktree({
|
|
5486
|
+
cwd: project.rootPath,
|
|
5487
|
+
worktreeSlug: basename(workspace.cwd),
|
|
5488
|
+
source: { kind: "checkout-branch", branchName: branch },
|
|
5489
|
+
runSetup: false,
|
|
5490
|
+
paseoHome: this.paseoHome,
|
|
5491
|
+
worktreesRoot: this.worktreesRoot,
|
|
5492
|
+
});
|
|
5493
|
+
}
|
|
5494
|
+
catch (error) {
|
|
5495
|
+
throw toWorktreeRequestError(error);
|
|
5496
|
+
}
|
|
5497
|
+
if (normalize(result.worktreePath) !== normalize(workspace.cwd)) {
|
|
5498
|
+
throw new WorktreeRequestError({
|
|
5499
|
+
code: "unknown",
|
|
5500
|
+
message: `Recreated worktree diverged from ${workspace.cwd}: ${result.worktreePath}`,
|
|
5501
|
+
});
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5343
5504
|
async ensureWorkspaceRecordUnarchived(workspace) {
|
|
5344
5505
|
const project = await this.projectRegistry.get(workspace.projectId);
|
|
5345
5506
|
if (!workspace.archivedAt && (!project || !project.archivedAt)) {
|
|
@@ -5501,21 +5662,10 @@ export class Session {
|
|
|
5501
5662
|
this.shouldSkipWorkspaceGitWatchUpdate(workspaceId, nextWorkspace)) {
|
|
5502
5663
|
continue;
|
|
5503
5664
|
}
|
|
5504
|
-
|
|
5505
|
-
if (watchTarget && this.onBranchChanged) {
|
|
5506
|
-
const newBranchName = nextWorkspace?.name ?? null;
|
|
5507
|
-
if (newBranchName !== watchTarget.lastBranchName) {
|
|
5508
|
-
this.onBranchChanged(workspaceId, watchTarget.lastBranchName, newBranchName);
|
|
5509
|
-
}
|
|
5510
|
-
}
|
|
5511
|
-
this.rememberWorkspaceGitDescriptorState(workspaceId, nextWorkspace);
|
|
5665
|
+
this.recordWorkspaceGitDescriptorState(workspaceId, nextWorkspace);
|
|
5512
5666
|
if (!nextWorkspace) {
|
|
5513
5667
|
subscription.lastEmittedByWorkspaceId.delete(workspaceId);
|
|
5514
|
-
this.bufferOrEmitWorkspaceUpdate(subscription,
|
|
5515
|
-
kind: "remove",
|
|
5516
|
-
id: workspaceId,
|
|
5517
|
-
...(await this.resolveEmptyProjectForArchivedWorkspace(workspaceId)),
|
|
5518
|
-
});
|
|
5668
|
+
this.bufferOrEmitWorkspaceUpdate(subscription, await this.buildWorkspaceRemoveUpdatePayload(workspaceId, options?.removedProjectId));
|
|
5519
5669
|
continue;
|
|
5520
5670
|
}
|
|
5521
5671
|
const nextPayload = {
|
|
@@ -5534,6 +5684,26 @@ export class Session {
|
|
|
5534
5684
|
void this.reconcileAndEmitWorkspaceUpdates();
|
|
5535
5685
|
}
|
|
5536
5686
|
}
|
|
5687
|
+
recordWorkspaceGitDescriptorState(workspaceId, nextWorkspace) {
|
|
5688
|
+
const watchTarget = this.resolveWorkspaceGitWatchTarget(workspaceId);
|
|
5689
|
+
if (watchTarget && this.onBranchChanged) {
|
|
5690
|
+
const newBranchName = nextWorkspace?.name ?? null;
|
|
5691
|
+
if (newBranchName !== watchTarget.lastBranchName) {
|
|
5692
|
+
this.onBranchChanged(workspaceId, watchTarget.lastBranchName, newBranchName);
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
this.rememberWorkspaceGitDescriptorState(workspaceId, nextWorkspace);
|
|
5696
|
+
}
|
|
5697
|
+
async buildWorkspaceRemoveUpdatePayload(workspaceId, removedProjectId) {
|
|
5698
|
+
if (removedProjectId) {
|
|
5699
|
+
return { kind: "remove", id: workspaceId, removedProjectId };
|
|
5700
|
+
}
|
|
5701
|
+
return {
|
|
5702
|
+
kind: "remove",
|
|
5703
|
+
id: workspaceId,
|
|
5704
|
+
...(await this.resolveEmptyProjectForArchivedWorkspace(workspaceId)),
|
|
5705
|
+
};
|
|
5706
|
+
}
|
|
5537
5707
|
// When a workspace is archived its project may become empty. Resolve the
|
|
5538
5708
|
// now-empty project parent so the `remove` update can carry it, keeping the
|
|
5539
5709
|
// sidebar's empty project row in sync without a full re-hydration.
|
|
@@ -45,8 +45,8 @@ export declare const LOCAL_STT_MODEL_IDS: LocalSttModelId[];
|
|
|
45
45
|
export declare const LOCAL_TTS_MODEL_IDS: LocalTtsModelId[];
|
|
46
46
|
export declare const DEFAULT_LOCAL_STT_MODEL: LocalSttModelId;
|
|
47
47
|
export declare const DEFAULT_LOCAL_TTS_MODEL: "kokoro-en-v0_19";
|
|
48
|
-
export declare const LocalSttModelIdSchema: z.ZodType<LocalSttModelId, z.
|
|
49
|
-
export declare const LocalTtsModelIdSchema: z.ZodType<"kokoro-en-v0_19", z.
|
|
48
|
+
export declare const LocalSttModelIdSchema: z.ZodType<LocalSttModelId, string, z.core.$ZodTypeInternals<LocalSttModelId, string>>;
|
|
49
|
+
export declare const LocalTtsModelIdSchema: z.ZodType<"kokoro-en-v0_19", string, z.core.$ZodTypeInternals<"kokoro-en-v0_19", string>>;
|
|
50
50
|
export type SherpaOnnxModelSpec = SherpaOnnxCatalogEntry & {
|
|
51
51
|
id: SherpaOnnxModelId;
|
|
52
52
|
};
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const SpeechProviderIdSchema: z.ZodEnum<
|
|
2
|
+
export declare const SpeechProviderIdSchema: z.ZodEnum<{
|
|
3
|
+
local: "local";
|
|
4
|
+
openai: "openai";
|
|
5
|
+
}>;
|
|
3
6
|
export type SpeechProviderId = z.infer<typeof SpeechProviderIdSchema>;
|
|
4
7
|
export declare const RequestedSpeechProviderSchema: z.ZodObject<{
|
|
5
|
-
provider: z.ZodEnum<
|
|
8
|
+
provider: z.ZodEnum<{
|
|
9
|
+
local: "local";
|
|
10
|
+
openai: "openai";
|
|
11
|
+
}>;
|
|
6
12
|
explicit: z.ZodBoolean;
|
|
7
13
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
-
},
|
|
9
|
-
provider: "local" | "openai";
|
|
10
|
-
explicit: boolean;
|
|
11
|
-
enabled?: boolean | undefined;
|
|
12
|
-
}, {
|
|
13
|
-
provider: "local" | "openai";
|
|
14
|
-
explicit: boolean;
|
|
15
|
-
enabled?: boolean | undefined;
|
|
16
|
-
}>;
|
|
14
|
+
}, z.core.$strip>;
|
|
17
15
|
export type RequestedSpeechProvider = z.infer<typeof RequestedSpeechProviderSchema>;
|
|
18
16
|
export interface RequestedSpeechProviders {
|
|
19
17
|
dictationStt: RequestedSpeechProvider;
|
|
@@ -807,6 +807,10 @@ export class VoiceAssistantWebSocketServer {
|
|
|
807
807
|
checkoutRefresh: true,
|
|
808
808
|
// COMPAT(workspaceMultiplicity): added in v0.1.97, drop the gate when floor >= v0.1.97
|
|
809
809
|
workspaceMultiplicity: true,
|
|
810
|
+
// COMPAT(projectRemove): added in v0.1.97, drop the gate when floor >= v0.1.97.
|
|
811
|
+
projectRemove: true,
|
|
812
|
+
// COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97
|
|
813
|
+
worktreeRestore: true,
|
|
810
814
|
},
|
|
811
815
|
};
|
|
812
816
|
}
|
|
@@ -4,65 +4,33 @@ import type { PersistedProjectKind, PersistedWorkspaceKind } from "./workspace-r
|
|
|
4
4
|
declare const PersistedProjectRecordSchema: z.ZodObject<{
|
|
5
5
|
projectId: z.ZodString;
|
|
6
6
|
rootPath: z.ZodString;
|
|
7
|
-
kind: z.ZodEnum<
|
|
7
|
+
kind: z.ZodEnum<{
|
|
8
|
+
git: "git";
|
|
9
|
+
non_git: "non_git";
|
|
10
|
+
}>;
|
|
8
11
|
displayName: z.ZodString;
|
|
9
|
-
customName: z.
|
|
12
|
+
customName: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
10
13
|
createdAt: z.ZodString;
|
|
11
14
|
updatedAt: z.ZodString;
|
|
12
15
|
archivedAt: z.ZodNullable<z.ZodString>;
|
|
13
|
-
},
|
|
14
|
-
kind: "git" | "non_git";
|
|
15
|
-
createdAt: string;
|
|
16
|
-
updatedAt: string;
|
|
17
|
-
archivedAt: string | null;
|
|
18
|
-
projectId: string;
|
|
19
|
-
displayName: string;
|
|
20
|
-
rootPath: string;
|
|
21
|
-
customName: string | null;
|
|
22
|
-
}, {
|
|
23
|
-
kind: "git" | "non_git";
|
|
24
|
-
createdAt: string;
|
|
25
|
-
updatedAt: string;
|
|
26
|
-
archivedAt: string | null;
|
|
27
|
-
projectId: string;
|
|
28
|
-
displayName: string;
|
|
29
|
-
rootPath: string;
|
|
30
|
-
customName?: string | null | undefined;
|
|
31
|
-
}>;
|
|
16
|
+
}, z.core.$strip>;
|
|
32
17
|
declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
|
|
33
18
|
workspaceId: z.ZodString;
|
|
34
19
|
projectId: z.ZodString;
|
|
35
20
|
cwd: z.ZodString;
|
|
36
|
-
kind: z.ZodEnum<
|
|
21
|
+
kind: z.ZodEnum<{
|
|
22
|
+
local_checkout: "local_checkout";
|
|
23
|
+
worktree: "worktree";
|
|
24
|
+
directory: "directory";
|
|
25
|
+
}>;
|
|
37
26
|
displayName: z.ZodString;
|
|
38
|
-
title: z.
|
|
39
|
-
branch: z.
|
|
27
|
+
title: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
28
|
+
branch: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
29
|
+
baseBranch: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
40
30
|
createdAt: z.ZodString;
|
|
41
31
|
updatedAt: z.ZodString;
|
|
42
32
|
archivedAt: z.ZodNullable<z.ZodString>;
|
|
43
|
-
},
|
|
44
|
-
kind: "local_checkout" | "worktree" | "directory";
|
|
45
|
-
workspaceId: string;
|
|
46
|
-
cwd: string;
|
|
47
|
-
title: string | null;
|
|
48
|
-
createdAt: string;
|
|
49
|
-
updatedAt: string;
|
|
50
|
-
archivedAt: string | null;
|
|
51
|
-
projectId: string;
|
|
52
|
-
displayName: string;
|
|
53
|
-
branch: string | null;
|
|
54
|
-
}, {
|
|
55
|
-
kind: "local_checkout" | "worktree" | "directory";
|
|
56
|
-
workspaceId: string;
|
|
57
|
-
cwd: string;
|
|
58
|
-
createdAt: string;
|
|
59
|
-
updatedAt: string;
|
|
60
|
-
archivedAt: string | null;
|
|
61
|
-
projectId: string;
|
|
62
|
-
displayName: string;
|
|
63
|
-
title?: string | null | undefined;
|
|
64
|
-
branch?: string | null | undefined;
|
|
65
|
-
}>;
|
|
33
|
+
}, z.core.$strip>;
|
|
66
34
|
export type PersistedProjectRecord = z.infer<typeof PersistedProjectRecordSchema>;
|
|
67
35
|
export type PersistedWorkspaceRecord = z.infer<typeof PersistedWorkspaceRecordSchema>;
|
|
68
36
|
export interface ProjectRegistry {
|
|
@@ -95,7 +63,7 @@ declare class FileBackedRegistry<TRecord extends RegistryRecord> {
|
|
|
95
63
|
constructor(options: {
|
|
96
64
|
filePath: string;
|
|
97
65
|
logger: Logger;
|
|
98
|
-
schema: z.ZodType<TRecord,
|
|
66
|
+
schema: z.ZodType<TRecord, unknown>;
|
|
99
67
|
getId: (record: TRecord) => string;
|
|
100
68
|
component: string;
|
|
101
69
|
});
|
|
@@ -135,6 +103,7 @@ export declare function createPersistedWorkspaceRecord(input: {
|
|
|
135
103
|
displayName: string;
|
|
136
104
|
title?: string | null;
|
|
137
105
|
branch?: string | null;
|
|
106
|
+
baseBranch?: string | null;
|
|
138
107
|
createdAt: string;
|
|
139
108
|
updatedAt: string;
|
|
140
109
|
archivedAt?: string | null;
|
|
@@ -39,6 +39,14 @@ const PersistedWorkspaceRecordSchema = z.object({
|
|
|
39
39
|
.nullable()
|
|
40
40
|
.optional()
|
|
41
41
|
.transform((value) => value ?? null),
|
|
42
|
+
// The base branch the worktree was created from (normalized like worktree.json's
|
|
43
|
+
// baseRefName). Only worktree workspaces carry a base branch; checkout-branch
|
|
44
|
+
// worktrees and directory/local_checkout workspaces leave it null.
|
|
45
|
+
baseBranch: z
|
|
46
|
+
.string()
|
|
47
|
+
.nullable()
|
|
48
|
+
.optional()
|
|
49
|
+
.transform((value) => value ?? null),
|
|
42
50
|
createdAt: z.string(),
|
|
43
51
|
updatedAt: z.string(),
|
|
44
52
|
archivedAt: z.string().nullable(),
|
|
@@ -170,6 +178,7 @@ export function createPersistedWorkspaceRecord(input) {
|
|
|
170
178
|
...input,
|
|
171
179
|
title: input.title ?? null,
|
|
172
180
|
branch: input.branch ?? null,
|
|
181
|
+
baseBranch: input.baseBranch ?? null,
|
|
173
182
|
archivedAt: input.archivedAt ?? null,
|
|
174
183
|
});
|
|
175
184
|
}
|
|
@@ -9,10 +9,15 @@ export interface TerminalSessionControllerOptions {
|
|
|
9
9
|
hasBinaryChannel: () => boolean;
|
|
10
10
|
isPathWithinRoot: (rootPath: string, candidatePath: string) => boolean;
|
|
11
11
|
sessionLogger: pino.Logger;
|
|
12
|
+
listTerminalWorkspaceRefs?: () => Promise<readonly TerminalWorkspaceRef[]>;
|
|
12
13
|
listTerminalWorkspaceRoots?: () => Promise<readonly string[]>;
|
|
13
14
|
clientSupportsWrapReflow?: () => boolean;
|
|
14
15
|
getClientBufferedAmount?: () => number | null;
|
|
15
16
|
}
|
|
17
|
+
interface TerminalWorkspaceRef {
|
|
18
|
+
workspaceId: string;
|
|
19
|
+
cwd: string;
|
|
20
|
+
}
|
|
16
21
|
export interface TerminalSessionControllerMetrics {
|
|
17
22
|
directorySubscriptionCount: number;
|
|
18
23
|
streamSubscriptionCount: number;
|
|
@@ -24,6 +29,7 @@ export declare class TerminalSessionController {
|
|
|
24
29
|
private readonly hasBinaryChannel;
|
|
25
30
|
private readonly isPathWithinRoot;
|
|
26
31
|
private readonly sessionLogger;
|
|
32
|
+
private readonly listTerminalWorkspaceRefs;
|
|
27
33
|
private readonly listTerminalWorkspaceRoots;
|
|
28
34
|
private readonly clientSupportsWrapReflow;
|
|
29
35
|
private readonly getClientBufferedAmount;
|
|
@@ -59,6 +65,7 @@ export declare class TerminalSessionController {
|
|
|
59
65
|
private resolveTerminalOwnerRoot;
|
|
60
66
|
private isSamePath;
|
|
61
67
|
private handleCreateTerminalRequest;
|
|
68
|
+
private resolveLegacyTerminalWorkspaceId;
|
|
62
69
|
private handleRenameTerminalRequest;
|
|
63
70
|
private handleSubscribeTerminalRequest;
|
|
64
71
|
private handleUnsubscribeTerminalRequest;
|
|
@@ -74,4 +81,5 @@ export declare class TerminalSessionController {
|
|
|
74
81
|
private allocateSlot;
|
|
75
82
|
private detachStream;
|
|
76
83
|
}
|
|
84
|
+
export {};
|
|
77
85
|
//# sourceMappingURL=terminal-session-controller.d.ts.map
|
|
@@ -34,7 +34,10 @@ export class TerminalSessionController {
|
|
|
34
34
|
this.hasBinaryChannel = options.hasBinaryChannel;
|
|
35
35
|
this.isPathWithinRoot = options.isPathWithinRoot;
|
|
36
36
|
this.sessionLogger = options.sessionLogger;
|
|
37
|
-
this.
|
|
37
|
+
this.listTerminalWorkspaceRefs = options.listTerminalWorkspaceRefs ?? (async () => []);
|
|
38
|
+
this.listTerminalWorkspaceRoots =
|
|
39
|
+
options.listTerminalWorkspaceRoots ??
|
|
40
|
+
(async () => (await this.listTerminalWorkspaceRefs()).map((workspace) => workspace.cwd));
|
|
38
41
|
this.clientSupportsWrapReflow = options.clientSupportsWrapReflow ?? (() => false);
|
|
39
42
|
this.getClientBufferedAmount = options.getClientBufferedAmount ?? (() => 0);
|
|
40
43
|
}
|
|
@@ -331,7 +334,8 @@ export class TerminalSessionController {
|
|
|
331
334
|
});
|
|
332
335
|
return;
|
|
333
336
|
}
|
|
334
|
-
|
|
337
|
+
const workspaceId = msg.workspaceId ?? (await this.resolveLegacyTerminalWorkspaceId(msg.cwd));
|
|
338
|
+
if (!workspaceId) {
|
|
335
339
|
this.emit({
|
|
336
340
|
type: "create_terminal_response",
|
|
337
341
|
payload: {
|
|
@@ -344,7 +348,7 @@ export class TerminalSessionController {
|
|
|
344
348
|
}
|
|
345
349
|
const session = await this.terminalManager.createTerminal({
|
|
346
350
|
cwd: msg.cwd,
|
|
347
|
-
workspaceId
|
|
351
|
+
workspaceId,
|
|
348
352
|
name: msg.name,
|
|
349
353
|
command: msg.command,
|
|
350
354
|
args: msg.args,
|
|
@@ -378,6 +382,22 @@ export class TerminalSessionController {
|
|
|
378
382
|
});
|
|
379
383
|
}
|
|
380
384
|
}
|
|
385
|
+
async resolveLegacyTerminalWorkspaceId(cwd) {
|
|
386
|
+
const workspaceRefs = await this.listTerminalWorkspaceRefs();
|
|
387
|
+
if (workspaceRefs.length === 0) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
const exactMatch = workspaceRefs.find((workspace) => this.isSamePath(workspace.cwd, cwd));
|
|
391
|
+
if (exactMatch) {
|
|
392
|
+
return exactMatch.workspaceId;
|
|
393
|
+
}
|
|
394
|
+
const ownerRoot = this.resolveTerminalOwnerRoot(cwd, workspaceRefs.map((workspace) => workspace.cwd));
|
|
395
|
+
if (!ownerRoot) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
return (workspaceRefs.find((workspace) => this.isSamePath(workspace.cwd, ownerRoot))?.workspaceId ??
|
|
399
|
+
null);
|
|
400
|
+
}
|
|
381
401
|
async handleRenameTerminalRequest(msg) {
|
|
382
402
|
const respond = (success, error) => {
|
|
383
403
|
this.emit({
|