@getpaseo/server 0.1.95 → 0.1.97-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/{utils/executable.d.ts → executable-resolution/executable-resolution.d.ts} +2 -2
- package/dist/server/{utils/executable.js → executable-resolution/executable-resolution.js} +16 -14
- package/dist/server/executable-resolution/windows.d.ts +18 -0
- package/dist/server/executable-resolution/windows.js +62 -0
- package/dist/server/server/agent/agent-loading.js +4 -1
- package/dist/server/server/agent/agent-manager.d.ts +10 -2
- package/dist/server/server/agent/agent-manager.js +34 -46
- package/dist/server/server/agent/agent-projections.js +3 -0
- package/dist/server/server/agent/agent-prompt.js +19 -1
- package/dist/server/server/agent/agent-response-loop.js +2 -4
- package/dist/server/server/agent/agent-storage.d.ts +18 -19
- package/dist/server/server/agent/agent-storage.js +6 -23
- package/dist/server/server/agent/create-agent/create.d.ts +2 -12
- package/dist/server/server/agent/create-agent/create.js +28 -30
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +4 -2
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +31 -22
- package/dist/server/server/agent/import-sessions.d.ts +1 -10
- package/dist/server/server/agent/import-sessions.js +1 -53
- package/dist/server/server/agent/lifecycle-command.js +5 -4
- package/dist/server/server/agent/mcp-server.d.ts +8 -5
- package/dist/server/server/agent/mcp-server.js +41 -14
- package/dist/server/server/agent/mcp-shared.d.ts +6 -3
- package/dist/server/server/agent/mcp-shared.js +3 -0
- package/dist/server/server/agent/provider-launch-config.js +1 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts +5 -0
- package/dist/server/server/agent/providers/acp-agent.js +31 -26
- package/dist/server/server/agent/providers/claude/agent.js +45 -6
- package/dist/server/server/agent/providers/codex-app-server-agent.js +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +1 -0
- package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +0 -7
- package/dist/server/server/agent/providers/cursor-acp-agent.js +0 -78
- 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 +73 -1
- package/dist/server/server/agent/providers/opencode/server-manager.js +1 -1
- package/dist/server/server/agent/structured-generation-providers.js +45 -1
- package/dist/server/server/agent-attention-policy.d.ts +12 -3
- package/dist/server/server/agent-attention-policy.js +15 -3
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +7 -6
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +21 -16
- package/dist/server/server/bootstrap.d.ts +3 -0
- package/dist/server/server/bootstrap.js +91 -12
- package/dist/server/server/config.js +1 -0
- package/dist/server/server/daemon-config-store.js +1 -0
- package/dist/server/server/exports.d.ts +1 -1
- package/dist/server/server/exports.js +1 -1
- package/dist/server/server/loop-service.d.ts +24 -24
- package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
- package/dist/server/server/migrations/backfill-workspace-id.migration.js +60 -0
- package/dist/server/server/paseo-worktree-service.d.ts +9 -0
- package/dist/server/server/paseo-worktree-service.js +71 -12
- package/dist/server/server/path-utils.d.ts +1 -0
- package/dist/server/server/path-utils.js +6 -1
- package/dist/server/server/persisted-config.d.ts +7 -0
- package/dist/server/server/persisted-config.js +1 -0
- package/dist/server/server/persistence-hooks.d.ts +1 -0
- package/dist/server/server/persistence-hooks.js +13 -5
- package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
- package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
- package/dist/server/server/script-proxy.d.ts +1 -1
- package/dist/server/server/script-proxy.js +1 -1
- package/dist/server/server/service-proxy.js +1 -1
- package/dist/server/server/session.d.ts +31 -6
- package/dist/server/server/session.js +640 -196
- package/dist/server/server/websocket-server.d.ts +5 -0
- package/dist/server/server/websocket-server.js +137 -3
- package/dist/server/server/workspace-archive-service.d.ts +60 -3
- package/dist/server/server/workspace-archive-service.js +217 -4
- package/dist/server/server/workspace-directory.d.ts +20 -2
- package/dist/server/server/workspace-directory.js +148 -70
- package/dist/server/server/workspace-git-service.js +21 -21
- package/dist/server/server/workspace-reconciliation-service.d.ts +1 -1
- package/dist/server/server/workspace-reconciliation-service.js +21 -22
- package/dist/server/server/workspace-registry-bootstrap.js +23 -10
- package/dist/server/server/workspace-registry-model.d.ts +3 -3
- package/dist/server/server/workspace-registry-model.js +9 -10
- package/dist/server/server/workspace-registry.d.ts +17 -4
- package/dist/server/server/workspace-registry.js +27 -0
- package/dist/server/server/worktree/commands.d.ts +7 -5
- package/dist/server/server/worktree/commands.js +38 -18
- package/dist/server/server/worktree-bootstrap.d.ts +1 -0
- package/dist/server/server/worktree-bootstrap.js +4 -1
- package/dist/server/server/worktree-branch-name-generator.d.ts +5 -1
- package/dist/server/server/worktree-branch-name-generator.js +8 -2
- package/dist/server/server/worktree-session.d.ts +4 -5
- package/dist/server/server/worktree-session.js +9 -3
- package/dist/server/services/github-service.js +1 -1
- package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +20 -0
- package/dist/server/terminal/activity/terminal-activity-tracker.js +59 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
- package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
- package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
- package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
- package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
- package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
- package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +46 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
- package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
- package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
- package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
- package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
- package/dist/server/terminal/terminal-manager-factory.d.ts +4 -1
- package/dist/server/terminal/terminal-manager-factory.js +2 -2
- package/dist/server/terminal/terminal-manager.d.ts +33 -2
- package/dist/server/terminal/terminal-manager.js +144 -18
- package/dist/server/terminal/terminal-output-coalescer.d.ts +4 -0
- package/dist/server/terminal/terminal-output-coalescer.js +18 -0
- package/dist/server/terminal/terminal-restore.d.ts +1 -0
- package/dist/server/terminal/terminal-restore.js +6 -0
- package/dist/server/terminal/terminal-session-controller.d.ts +4 -2
- package/dist/server/terminal/terminal-session-controller.js +65 -24
- package/dist/server/terminal/terminal-worker-process.js +146 -63
- package/dist/server/terminal/terminal-worker-protocol.d.ts +19 -14
- package/dist/server/terminal/terminal.d.ts +42 -0
- package/dist/server/terminal/terminal.js +235 -16
- package/dist/server/terminal/worker-terminal-manager.d.ts +1 -0
- package/dist/server/terminal/worker-terminal-manager.js +220 -36
- package/dist/server/utils/build-metadata-prompt.d.ts +1 -1
- package/dist/server/utils/github-remote.js +1 -1
- package/dist/server/utils/tree-kill.d.ts +2 -2
- package/dist/src/{utils/executable.js → executable-resolution/executable-resolution.js} +16 -14
- package/dist/src/executable-resolution/windows.js +62 -0
- package/dist/src/server/agent/provider-launch-config.js +1 -1
- package/dist/src/server/persisted-config.js +1 -0
- package/package.json +10 -5
- package/dist/server/server/agent/agent-metadata-generator.d.ts +0 -36
- package/dist/server/server/agent/agent-metadata-generator.js +0 -112
- package/dist/server/server/paseo-worktree-archive-service.d.ts +0 -41
- package/dist/server/server/paseo-worktree-archive-service.js +0 -144
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import { fork } from "node:child_process";
|
|
2
1
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { fork } from "node:child_process";
|
|
3
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
4
|
+
import { assertAbsolutePath, isSameOrDescendantPath } from "../server/path-utils.js";
|
|
5
|
+
import { deriveTerminalActivityStatusBucket } from "@getpaseo/protocol/terminal-activity";
|
|
5
6
|
const REQUEST_TIMEOUT_MS = 10000;
|
|
7
|
+
function requiredWorkspaceId(workspaceId) {
|
|
8
|
+
if (workspaceId === undefined) {
|
|
9
|
+
throw new Error("workspaceId is required");
|
|
10
|
+
}
|
|
11
|
+
return workspaceId;
|
|
12
|
+
}
|
|
13
|
+
function asRequiredWorkerTerminalInfo(info) {
|
|
14
|
+
requiredWorkspaceId(info.workspaceId);
|
|
15
|
+
return info;
|
|
16
|
+
}
|
|
17
|
+
function createActivityToken() {
|
|
18
|
+
return randomBytes(32).toString("base64url");
|
|
19
|
+
}
|
|
6
20
|
function resolveWorkerUrl() {
|
|
7
21
|
const currentUrl = import.meta.url;
|
|
8
22
|
if (currentUrl.endsWith(".ts")) {
|
|
@@ -34,7 +48,9 @@ function cloneTerminalInfo(info) {
|
|
|
34
48
|
id: info.id,
|
|
35
49
|
name: info.name,
|
|
36
50
|
cwd: info.cwd,
|
|
51
|
+
workspaceId: info.workspaceId,
|
|
37
52
|
...(info.title ? { title: info.title } : {}),
|
|
53
|
+
activity: info.activity,
|
|
38
54
|
};
|
|
39
55
|
}
|
|
40
56
|
function forkTerminalWorker() {
|
|
@@ -50,7 +66,10 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
50
66
|
const pendingRequests = new Map();
|
|
51
67
|
const recordsById = new Map();
|
|
52
68
|
const terminalIdsByCwd = new Map();
|
|
69
|
+
const terminalActivityTokenById = new Map();
|
|
53
70
|
const terminalsChangedListeners = new Set();
|
|
71
|
+
const terminalActivityListeners = new Set();
|
|
72
|
+
const terminalWorkspaceContributionChangedListeners = new Set();
|
|
54
73
|
let workerExited = false;
|
|
55
74
|
let workerShutdownTimer = null;
|
|
56
75
|
function emitTerminalsChanged(event) {
|
|
@@ -63,6 +82,26 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
63
82
|
}
|
|
64
83
|
}
|
|
65
84
|
}
|
|
85
|
+
function emitTerminalWorkspaceContributionChanged(event) {
|
|
86
|
+
for (const listener of Array.from(terminalWorkspaceContributionChangedListeners)) {
|
|
87
|
+
try {
|
|
88
|
+
listener(event);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// no-op
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function emitTerminalActivityTransition(event) {
|
|
96
|
+
for (const listener of Array.from(terminalActivityListeners)) {
|
|
97
|
+
try {
|
|
98
|
+
listener(event);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// no-op
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
66
105
|
function listTerminalItemsForCwd(cwd) {
|
|
67
106
|
const terminalIds = terminalIdsByCwd.get(cwd);
|
|
68
107
|
if (!terminalIds) {
|
|
@@ -78,7 +117,9 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
78
117
|
id: record.info.id,
|
|
79
118
|
name: record.info.name,
|
|
80
119
|
cwd: record.info.cwd,
|
|
120
|
+
workspaceId: record.info.workspaceId,
|
|
81
121
|
...(record.info.title ? { title: record.info.title } : {}),
|
|
122
|
+
activity: record.activity,
|
|
82
123
|
});
|
|
83
124
|
}
|
|
84
125
|
return terminals;
|
|
@@ -93,12 +134,14 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
93
134
|
const record = {
|
|
94
135
|
info: cloneTerminalInfo(input.info),
|
|
95
136
|
state: input.state,
|
|
96
|
-
|
|
137
|
+
activity: input.info.activity,
|
|
138
|
+
replayPreamble: "",
|
|
97
139
|
exitInfo: null,
|
|
98
140
|
messageListeners: new Set(),
|
|
99
141
|
exitListeners: new Set(),
|
|
100
142
|
commandFinishedListeners: new Set(),
|
|
101
143
|
titleChangeListeners: new Set(),
|
|
144
|
+
activityChangeListeners: new Set(),
|
|
102
145
|
session: undefined,
|
|
103
146
|
};
|
|
104
147
|
const session = {
|
|
@@ -111,6 +154,9 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
111
154
|
get cwd() {
|
|
112
155
|
return record.info.cwd;
|
|
113
156
|
},
|
|
157
|
+
get workspaceId() {
|
|
158
|
+
return record.info.workspaceId;
|
|
159
|
+
},
|
|
114
160
|
send(message) {
|
|
115
161
|
if (message.type === "resize") {
|
|
116
162
|
record.state = {
|
|
@@ -163,6 +209,27 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
163
209
|
record.titleChangeListeners.delete(listener);
|
|
164
210
|
};
|
|
165
211
|
},
|
|
212
|
+
onActivityChange(listener) {
|
|
213
|
+
record.activityChangeListeners.add(listener);
|
|
214
|
+
return () => {
|
|
215
|
+
record.activityChangeListeners.delete(listener);
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
getActivity() {
|
|
219
|
+
return record.activity;
|
|
220
|
+
},
|
|
221
|
+
setActivity(state) {
|
|
222
|
+
record.activity = { state, changedAt: Date.now() };
|
|
223
|
+
sendBestEffortRequest({ type: "setActivity", terminalId: record.info.id, state });
|
|
224
|
+
},
|
|
225
|
+
clearActivityAttention() {
|
|
226
|
+
if (record.activity?.attentionReason == null) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
record.activity = { state: record.activity.state, changedAt: Date.now() };
|
|
230
|
+
sendBestEffortRequest({ type: "clearAttention", terminalId: record.info.id });
|
|
231
|
+
return true;
|
|
232
|
+
},
|
|
166
233
|
getSize() {
|
|
167
234
|
return {
|
|
168
235
|
rows: record.state.rows,
|
|
@@ -183,7 +250,12 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
183
250
|
};
|
|
184
251
|
},
|
|
185
252
|
getReplayPreamble() {
|
|
186
|
-
|
|
253
|
+
// Refreshed from every getTerminalState response, which the controller fetches
|
|
254
|
+
// before every snapshot replay (legacy + visible-snapshot restore). The one
|
|
255
|
+
// gap is restore.mode === "live", which replays without fetching state — there
|
|
256
|
+
// this can be stale/empty. No client sends "live" today; revisit (ship the
|
|
257
|
+
// preamble on the worker's snapshotReady) if one ever does.
|
|
258
|
+
return record.replayPreamble;
|
|
187
259
|
},
|
|
188
260
|
getTitle() {
|
|
189
261
|
return record.info.title;
|
|
@@ -225,6 +297,7 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
225
297
|
return undefined;
|
|
226
298
|
}
|
|
227
299
|
recordsById.delete(terminalId);
|
|
300
|
+
terminalActivityTokenById.delete(terminalId);
|
|
228
301
|
const terminalIds = terminalIdsByCwd.get(record.info.cwd);
|
|
229
302
|
if (terminalIds) {
|
|
230
303
|
terminalIds.delete(terminalId);
|
|
@@ -242,8 +315,8 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
242
315
|
if (message.message.type === "snapshot") {
|
|
243
316
|
record.state = message.message.state;
|
|
244
317
|
}
|
|
245
|
-
if (message.message.type === "
|
|
246
|
-
record.
|
|
318
|
+
if (message.message.type === "snapshotReady" && message.message.replayPreamble !== undefined) {
|
|
319
|
+
record.replayPreamble = message.message.replayPreamble;
|
|
247
320
|
}
|
|
248
321
|
for (const listener of Array.from(record.messageListeners)) {
|
|
249
322
|
listener(message.message);
|
|
@@ -259,7 +332,15 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
259
332
|
listener(message.info);
|
|
260
333
|
}
|
|
261
334
|
record.exitListeners.clear();
|
|
262
|
-
|
|
335
|
+
const previousBucket = deriveTerminalActivityStatusBucket(record.activity);
|
|
336
|
+
const removedRecord = removeRecord(message.terminalId);
|
|
337
|
+
if (previousBucket !== null && removedRecord) {
|
|
338
|
+
emitTerminalWorkspaceContributionChanged({
|
|
339
|
+
terminalId: removedRecord.info.id,
|
|
340
|
+
cwd: removedRecord.info.cwd,
|
|
341
|
+
workspaceId: removedRecord.info.workspaceId,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
263
344
|
emitTerminalsChanged({
|
|
264
345
|
cwd: record.info.cwd,
|
|
265
346
|
terminals: listTerminalItemsForCwd(record.info.cwd),
|
|
@@ -299,28 +380,52 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
299
380
|
listener(message.info);
|
|
300
381
|
}
|
|
301
382
|
}
|
|
302
|
-
function
|
|
383
|
+
function handleTerminalActivityChangeEvent(message) {
|
|
384
|
+
const record = recordsById.get(message.terminalId);
|
|
385
|
+
if (!record) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const previousActivity = record.activity;
|
|
389
|
+
record.activity = message.activity;
|
|
390
|
+
const transition = {
|
|
391
|
+
activity: message.activity,
|
|
392
|
+
previous: message.previous,
|
|
393
|
+
};
|
|
394
|
+
for (const listener of Array.from(record.activityChangeListeners)) {
|
|
395
|
+
listener(transition);
|
|
396
|
+
}
|
|
397
|
+
emitTerminalActivityTransition({
|
|
398
|
+
terminalId: record.info.id,
|
|
399
|
+
name: record.info.name,
|
|
400
|
+
cwd: record.info.cwd,
|
|
401
|
+
workspaceId: record.info.workspaceId,
|
|
402
|
+
activity: message.activity,
|
|
403
|
+
previous: message.previous,
|
|
404
|
+
});
|
|
405
|
+
const previousBucket = deriveTerminalActivityStatusBucket(previousActivity);
|
|
406
|
+
const nextBucket = deriveTerminalActivityStatusBucket(message.activity);
|
|
407
|
+
if (previousBucket !== nextBucket) {
|
|
408
|
+
emitTerminalWorkspaceContributionChanged({
|
|
409
|
+
terminalId: record.info.id,
|
|
410
|
+
cwd: record.info.cwd,
|
|
411
|
+
workspaceId: record.info.workspaceId,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
303
414
|
emitTerminalsChanged({
|
|
304
|
-
cwd:
|
|
305
|
-
terminals:
|
|
306
|
-
id: terminal.id,
|
|
307
|
-
name: terminal.name,
|
|
308
|
-
cwd: terminal.cwd,
|
|
309
|
-
...(terminal.title ? { title: terminal.title } : {}),
|
|
310
|
-
})),
|
|
415
|
+
cwd: record.info.cwd,
|
|
416
|
+
terminals: listTerminalItemsForCwd(record.info.cwd),
|
|
311
417
|
});
|
|
312
418
|
}
|
|
313
419
|
function handleWorkerEvent(message) {
|
|
314
420
|
switch (message.type) {
|
|
315
421
|
case "terminalCreated": {
|
|
316
|
-
registerRecord({
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
removeRecord(message.terminalId);
|
|
422
|
+
registerRecord({
|
|
423
|
+
info: asRequiredWorkerTerminalInfo(message.terminal),
|
|
424
|
+
state: message.state,
|
|
425
|
+
});
|
|
321
426
|
emitTerminalsChanged({
|
|
322
|
-
cwd: message.cwd,
|
|
323
|
-
terminals: listTerminalItemsForCwd(message.cwd),
|
|
427
|
+
cwd: message.terminal.cwd,
|
|
428
|
+
terminals: listTerminalItemsForCwd(message.terminal.cwd),
|
|
324
429
|
});
|
|
325
430
|
return;
|
|
326
431
|
}
|
|
@@ -340,8 +445,8 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
340
445
|
handleTerminalCommandFinishedEvent(message);
|
|
341
446
|
return;
|
|
342
447
|
}
|
|
343
|
-
case "
|
|
344
|
-
|
|
448
|
+
case "terminalActivityChange": {
|
|
449
|
+
handleTerminalActivityChangeEvent(message);
|
|
345
450
|
return;
|
|
346
451
|
}
|
|
347
452
|
}
|
|
@@ -408,19 +513,56 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
408
513
|
// lifecycle state; do not let fire-and-forget sends crash the daemon.
|
|
409
514
|
});
|
|
410
515
|
}
|
|
411
|
-
function toSessions(terminals) {
|
|
412
|
-
return terminals
|
|
413
|
-
.map((terminal) => recordsById.get(terminal.id)?.session)
|
|
414
|
-
.filter((session) => Boolean(session));
|
|
415
|
-
}
|
|
416
516
|
return {
|
|
417
|
-
async getTerminals(cwd) {
|
|
418
|
-
|
|
419
|
-
|
|
517
|
+
async getTerminals(cwd, options) {
|
|
518
|
+
assertAbsolutePath(cwd);
|
|
519
|
+
// Served from the local mirror, exactly like every other parent read.
|
|
520
|
+
// Terminals are bucketed by exact cwd, but an agent can open a terminal in
|
|
521
|
+
// a subdirectory of the workspace. A query for the workspace root must
|
|
522
|
+
// surface those too, so aggregate every bucket at or below `cwd`.
|
|
523
|
+
const sessions = [];
|
|
524
|
+
for (const [bucketCwd, terminalIds] of terminalIdsByCwd) {
|
|
525
|
+
if (!isSameOrDescendantPath(cwd, bucketCwd)) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
for (const terminalId of terminalIds) {
|
|
529
|
+
const session = recordsById.get(terminalId)?.session;
|
|
530
|
+
if (session) {
|
|
531
|
+
sessions.push(session);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
// When the query carries a workspaceId, two workspaces sharing a cwd must
|
|
536
|
+
// not see each other's terminals. A missing owner is not workspace
|
|
537
|
+
// membership; unscoped callers can still list those legacy terminals.
|
|
538
|
+
if (options?.workspaceId !== undefined) {
|
|
539
|
+
return sessions.filter((session) => session.workspaceId === options.workspaceId);
|
|
540
|
+
}
|
|
541
|
+
return sessions;
|
|
420
542
|
},
|
|
421
543
|
async createTerminal(options) {
|
|
422
|
-
const
|
|
423
|
-
|
|
544
|
+
const terminalId = options.id ?? randomUUID();
|
|
545
|
+
const activityToken = createActivityToken();
|
|
546
|
+
const terminalActivityUrl = managerOptions.getTerminalActivityUrl?.() ?? null;
|
|
547
|
+
terminalActivityTokenById.set(terminalId, activityToken);
|
|
548
|
+
let result;
|
|
549
|
+
try {
|
|
550
|
+
result = (await sendRequest({
|
|
551
|
+
type: "createTerminal",
|
|
552
|
+
options: {
|
|
553
|
+
...options,
|
|
554
|
+
id: terminalId,
|
|
555
|
+
activityToken,
|
|
556
|
+
activityUrl: terminalActivityUrl,
|
|
557
|
+
},
|
|
558
|
+
}));
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
terminalActivityTokenById.delete(terminalId);
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
const session = registerRecord({ info: result.terminal, state: result.state });
|
|
565
|
+
return session;
|
|
424
566
|
},
|
|
425
567
|
registerCwdEnv(options) {
|
|
426
568
|
sendBestEffortRequest({
|
|
@@ -429,15 +571,29 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
429
571
|
env: options.env,
|
|
430
572
|
});
|
|
431
573
|
},
|
|
574
|
+
validateTerminalActivityToken(terminalId, token) {
|
|
575
|
+
const expected = terminalActivityTokenById.get(terminalId);
|
|
576
|
+
if (!expected) {
|
|
577
|
+
return "unknown";
|
|
578
|
+
}
|
|
579
|
+
return expected === token ? "valid" : "invalid";
|
|
580
|
+
},
|
|
432
581
|
getTerminal(id) {
|
|
433
582
|
return recordsById.get(id)?.session;
|
|
434
583
|
},
|
|
435
584
|
async getTerminalState(id, options) {
|
|
436
|
-
|
|
585
|
+
const snapshot = (await sendRequest({
|
|
437
586
|
type: "getTerminalState",
|
|
438
587
|
terminalId: id,
|
|
439
588
|
...(options ? { options } : {}),
|
|
440
589
|
}));
|
|
590
|
+
if (snapshot && snapshot.replayPreamble !== undefined) {
|
|
591
|
+
const record = recordsById.get(id);
|
|
592
|
+
if (record) {
|
|
593
|
+
record.replayPreamble = snapshot.replayPreamble;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return snapshot;
|
|
441
597
|
},
|
|
442
598
|
setTerminalTitle(id, title) {
|
|
443
599
|
const session = recordsById.get(id)?.session;
|
|
@@ -447,6 +603,22 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
447
603
|
session.setTitle(title);
|
|
448
604
|
return true;
|
|
449
605
|
},
|
|
606
|
+
async setTerminalActivity(id, state) {
|
|
607
|
+
const record = recordsById.get(id);
|
|
608
|
+
if (!record) {
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
await sendRequest({ type: "setActivity", terminalId: id, state });
|
|
612
|
+
return true;
|
|
613
|
+
},
|
|
614
|
+
async clearTerminalAttention(id) {
|
|
615
|
+
const record = recordsById.get(id);
|
|
616
|
+
if (!record || record.activity?.attentionReason == null) {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
await sendRequest({ type: "clearAttention", terminalId: id });
|
|
620
|
+
return true;
|
|
621
|
+
},
|
|
450
622
|
killTerminal(id) {
|
|
451
623
|
void sendRequest({ type: "killTerminal", terminalId: id }).catch(() => {
|
|
452
624
|
// no-op; kill is intentionally best-effort and synchronous in the public interface.
|
|
@@ -496,6 +668,18 @@ export function createWorkerTerminalManager(managerOptions = {}) {
|
|
|
496
668
|
terminalsChangedListeners.delete(listener);
|
|
497
669
|
};
|
|
498
670
|
},
|
|
671
|
+
subscribeTerminalActivity(listener) {
|
|
672
|
+
terminalActivityListeners.add(listener);
|
|
673
|
+
return () => {
|
|
674
|
+
terminalActivityListeners.delete(listener);
|
|
675
|
+
};
|
|
676
|
+
},
|
|
677
|
+
subscribeTerminalWorkspaceContributionChanged(listener) {
|
|
678
|
+
terminalWorkspaceContributionChangedListeners.add(listener);
|
|
679
|
+
return () => {
|
|
680
|
+
terminalWorkspaceContributionChangedListeners.delete(listener);
|
|
681
|
+
};
|
|
682
|
+
},
|
|
499
683
|
};
|
|
500
684
|
}
|
|
501
685
|
export function terminateWorkerTerminalManager(manager) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isGitHubHost, normalizeHost, parseGitHubRemoteIdentity, parseGitRemoteLocation, } from "@getpaseo/protocol/git-remote";
|
|
2
|
-
import { findExecutable } from "
|
|
2
|
+
import { findExecutable } from "../executable-resolution/executable-resolution.js";
|
|
3
3
|
import { execCommand } from "./spawn.js";
|
|
4
4
|
let sshExecutableLookup = null;
|
|
5
5
|
const sshHostnameResolutionCache = new Map();
|
|
@@ -5,7 +5,7 @@ export interface TreeKillTarget {
|
|
|
5
5
|
kill(signal?: NodeJS.Signals | number): boolean;
|
|
6
6
|
once?(event: "exit", listener: () => void): unknown;
|
|
7
7
|
}
|
|
8
|
-
interface TerminateWithTreeKillOptions {
|
|
8
|
+
export interface TerminateWithTreeKillOptions {
|
|
9
9
|
gracefulSignal?: NodeJS.Signals;
|
|
10
10
|
forceSignal?: NodeJS.Signals;
|
|
11
11
|
gracefulTimeoutMs: number;
|
|
@@ -13,6 +13,6 @@ interface TerminateWithTreeKillOptions {
|
|
|
13
13
|
onForceSignal?: () => void;
|
|
14
14
|
}
|
|
15
15
|
export type TerminateWithTreeKillResult = "already-exited" | "terminated" | "killed" | "kill-timeout";
|
|
16
|
+
export type ProcessTerminator = (child: TreeKillTarget, options: TerminateWithTreeKillOptions) => Promise<TerminateWithTreeKillResult>;
|
|
16
17
|
export declare function terminateWithTreeKill(child: TreeKillTarget, options: TerminateWithTreeKillOptions): Promise<TerminateWithTreeKillResult>;
|
|
17
|
-
export {};
|
|
18
18
|
//# sourceMappingURL=tree-kill.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
export { quoteWindowsArgument, quoteWindowsCommand } from "
|
|
3
|
+
import { execCommand } from "../utils/spawn.js";
|
|
4
|
+
import { isWindowsCommandScript } from "../utils/windows-command.js";
|
|
5
|
+
import { windowsExecutableResolution } from "./windows.js";
|
|
6
|
+
export { quoteWindowsArgument, quoteWindowsCommand } from "../utils/windows-command.js";
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
const which = require("which");
|
|
9
9
|
const PROBE_TIMEOUT_MS = 2000;
|
|
@@ -83,22 +83,24 @@ function classifyProbeError(error) {
|
|
|
83
83
|
* Check a literal executable path. PATH search is handled by findExecutable().
|
|
84
84
|
*/
|
|
85
85
|
export function executableExists(executablePath, exists = existsSync) {
|
|
86
|
-
if (
|
|
87
|
-
return executablePath;
|
|
88
|
-
if (process.platform === "win32" && !extname(executablePath)) {
|
|
89
|
-
for (const ext of [".exe", ".cmd"]) {
|
|
90
|
-
const candidate = executablePath + ext;
|
|
91
|
-
if (exists(candidate))
|
|
92
|
-
return candidate;
|
|
93
|
-
}
|
|
86
|
+
if (process.platform === "win32") {
|
|
87
|
+
return windowsExecutableResolution.exists(executablePath, { exists });
|
|
94
88
|
}
|
|
95
|
-
return null;
|
|
89
|
+
return exists(executablePath) ? executablePath : null;
|
|
96
90
|
}
|
|
97
91
|
export async function findExecutable(name, probeTimeoutMs = PROBE_TIMEOUT_MS) {
|
|
98
92
|
const trimmed = name.trim();
|
|
99
93
|
if (!trimmed) {
|
|
100
94
|
return null;
|
|
101
95
|
}
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
return windowsExecutableResolution.find(trimmed, {
|
|
98
|
+
enumeratePathCandidates: enumerateCandidates,
|
|
99
|
+
probeExecutable,
|
|
100
|
+
exists: existsSync,
|
|
101
|
+
probeTimeoutMs,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
102
104
|
if (hasPathSeparator(trimmed)) {
|
|
103
105
|
return (await probeExecutable(trimmed, probeTimeoutMs)) ? trimmed : null;
|
|
104
106
|
}
|
|
@@ -113,4 +115,4 @@ export async function findExecutable(name, probeTimeoutMs = PROBE_TIMEOUT_MS) {
|
|
|
113
115
|
export async function isCommandAvailable(command) {
|
|
114
116
|
return (await findExecutable(command)) !== null;
|
|
115
117
|
}
|
|
116
|
-
//# sourceMappingURL=executable.js.map
|
|
118
|
+
//# sourceMappingURL=executable-resolution.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
import { extname, join } from "node:path";
|
|
3
|
+
function hasPathSeparator(value) {
|
|
4
|
+
return value.includes("/") || value.includes("\\");
|
|
5
|
+
}
|
|
6
|
+
function enumerateLiteralPathCandidates(executablePath) {
|
|
7
|
+
if (extname(executablePath)) {
|
|
8
|
+
return [executablePath];
|
|
9
|
+
}
|
|
10
|
+
return [executablePath, `${executablePath}.exe`, `${executablePath}.cmd`];
|
|
11
|
+
}
|
|
12
|
+
function enumerateWingetPackageCandidates(name, localAppData) {
|
|
13
|
+
if (!localAppData) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const wingetPackages = join(localAppData, "Microsoft", "WinGet", "Packages");
|
|
17
|
+
let packageDirs;
|
|
18
|
+
try {
|
|
19
|
+
packageDirs = readdirSync(wingetPackages, { withFileTypes: true })
|
|
20
|
+
.filter((entry) => entry.isDirectory())
|
|
21
|
+
.map((entry) => entry.name);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const exeName = `${name}.exe`;
|
|
27
|
+
return packageDirs.map((packageDir) => join(wingetPackages, packageDir, exeName));
|
|
28
|
+
}
|
|
29
|
+
async function find(input, options) {
|
|
30
|
+
if (hasPathSeparator(input)) {
|
|
31
|
+
return findFirstProbeable(enumerateLiteralPathCandidates(input), options);
|
|
32
|
+
}
|
|
33
|
+
const pathCandidates = await options.enumeratePathCandidates(input);
|
|
34
|
+
const wingetCandidates = enumerateWingetPackageCandidates(input, options.localAppData ?? process.env.LOCALAPPDATA).filter(options.exists);
|
|
35
|
+
return findFirstProbeable([...pathCandidates, ...wingetCandidates], options);
|
|
36
|
+
}
|
|
37
|
+
async function findFirstProbeable(candidates, options) {
|
|
38
|
+
const seen = new Set();
|
|
39
|
+
for (const candidate of candidates) {
|
|
40
|
+
if (seen.has(candidate)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
seen.add(candidate);
|
|
44
|
+
if (await options.probeExecutable(candidate, options.probeTimeoutMs)) {
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
function exists(executablePath, options) {
|
|
51
|
+
for (const candidate of enumerateLiteralPathCandidates(executablePath)) {
|
|
52
|
+
if (options.exists(candidate)) {
|
|
53
|
+
return candidate;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
export const windowsExecutableResolution = {
|
|
59
|
+
exists,
|
|
60
|
+
find,
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=windows.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isAbsolute } from "node:path";
|
|
2
|
-
import { executableExists, findExecutable } from "../../
|
|
2
|
+
import { executableExists, findExecutable, } from "../../executable-resolution/executable-resolution.js";
|
|
3
3
|
import { createExternalProcessEnv } from "../paseo-env.js";
|
|
4
4
|
export { AgentProviderRuntimeSettingsMapSchema, ProviderCommandSchema, ProviderOverrideSchema, ProviderOverridesSchema, ProviderProfileModelSchema, ProviderRuntimeSettingsSchema, } from "@getpaseo/protocol/provider-config";
|
|
5
5
|
import { ProviderOverrideSchema, ProviderOverridesSchema, ProviderRuntimeSettingsSchema, } from "@getpaseo/protocol/provider-config";
|
|
@@ -187,6 +187,7 @@ export const PersistedConfigSchema = z
|
|
|
187
187
|
.passthrough()
|
|
188
188
|
.optional(),
|
|
189
189
|
autoArchiveAfterMerge: z.boolean().optional(),
|
|
190
|
+
enableTerminalAgentHooks: z.boolean().optional(),
|
|
190
191
|
appendSystemPrompt: z.string().optional(),
|
|
191
192
|
terminalProfiles: z.array(TerminalProfileSchema).optional(),
|
|
192
193
|
cors: z
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getpaseo/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.97-beta.1",
|
|
4
4
|
"description": "Paseo backend server",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/server",
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"types": "./dist/server/utils/tool-call-parsers.d.ts",
|
|
23
23
|
"source": "./src/utils/tool-call-parsers.ts",
|
|
24
24
|
"default": "./dist/server/utils/tool-call-parsers.js"
|
|
25
|
+
},
|
|
26
|
+
"./agent-hooks": {
|
|
27
|
+
"types": "./dist/server/terminal/agent-hooks/provider-registry.d.ts",
|
|
28
|
+
"source": "./src/terminal/agent-hooks/provider-registry.ts",
|
|
29
|
+
"default": "./dist/server/terminal/agent-hooks/provider-registry.js"
|
|
25
30
|
}
|
|
26
31
|
},
|
|
27
32
|
"publishConfig": {
|
|
@@ -59,10 +64,10 @@
|
|
|
59
64
|
"dependencies": {
|
|
60
65
|
"@agentclientprotocol/sdk": "^0.17.1",
|
|
61
66
|
"@anthropic-ai/claude-agent-sdk": "^0.2.133",
|
|
62
|
-
"@getpaseo/client": "0.1.
|
|
63
|
-
"@getpaseo/highlight": "0.1.
|
|
64
|
-
"@getpaseo/protocol": "0.1.
|
|
65
|
-
"@getpaseo/relay": "0.1.
|
|
67
|
+
"@getpaseo/client": "0.1.97-beta.1",
|
|
68
|
+
"@getpaseo/highlight": "0.1.97-beta.1",
|
|
69
|
+
"@getpaseo/protocol": "0.1.97-beta.1",
|
|
70
|
+
"@getpaseo/relay": "0.1.97-beta.1",
|
|
66
71
|
"@isaacs/ttlcache": "^2.1.4",
|
|
67
72
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
|
68
73
|
"@opencode-ai/sdk": "1.14.46",
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { Logger } from "pino";
|
|
2
|
-
import type { AgentManager } from "./agent-manager.js";
|
|
3
|
-
import { generateStructuredAgentResponseWithFallback } from "./agent-response-loop.js";
|
|
4
|
-
import { type StructuredGenerationDaemonConfig } from "./structured-generation-providers.js";
|
|
5
|
-
import type { WorkspaceGitService } from "../workspace-git-service.js";
|
|
6
|
-
import type { ProviderSnapshotManager } from "./provider-snapshot-manager.js";
|
|
7
|
-
export interface AgentMetadataGeneratorDeps {
|
|
8
|
-
generateStructuredAgentResponseWithFallback?: typeof generateStructuredAgentResponseWithFallback;
|
|
9
|
-
}
|
|
10
|
-
export interface AgentMetadataGenerationOptions {
|
|
11
|
-
agentManager: AgentManager;
|
|
12
|
-
agentId: string;
|
|
13
|
-
cwd: string;
|
|
14
|
-
workspaceGitService?: Pick<WorkspaceGitService, "resolveRepoRoot">;
|
|
15
|
-
providerSnapshotManager?: Pick<ProviderSnapshotManager, "listProviders">;
|
|
16
|
-
daemonConfig?: StructuredGenerationDaemonConfig | null;
|
|
17
|
-
currentSelection?: {
|
|
18
|
-
provider?: string | null;
|
|
19
|
-
model?: string | null;
|
|
20
|
-
thinkingOptionId?: string | null;
|
|
21
|
-
};
|
|
22
|
-
initialPrompt?: string | null;
|
|
23
|
-
explicitTitle?: string | null;
|
|
24
|
-
paseoHome?: string;
|
|
25
|
-
logger: Logger;
|
|
26
|
-
deps?: AgentMetadataGeneratorDeps;
|
|
27
|
-
}
|
|
28
|
-
interface AgentMetadataNeeds {
|
|
29
|
-
prompt: string | null;
|
|
30
|
-
needsTitle: boolean;
|
|
31
|
-
}
|
|
32
|
-
export declare function determineAgentMetadataNeeds(options: Pick<AgentMetadataGenerationOptions, "initialPrompt" | "explicitTitle" | "cwd" | "paseoHome" | "deps">): Promise<AgentMetadataNeeds>;
|
|
33
|
-
export declare function generateAndApplyAgentMetadata(options: AgentMetadataGenerationOptions): Promise<void>;
|
|
34
|
-
export declare function scheduleAgentMetadataGeneration(options: AgentMetadataGenerationOptions): void;
|
|
35
|
-
export {};
|
|
36
|
-
//# sourceMappingURL=agent-metadata-generator.d.ts.map
|