@getpaseo/server 0.1.101 → 0.1.102-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scripts/supervisor.js +26 -8
- package/dist/server/server/agent/activity-curator.d.ts +17 -0
- package/dist/server/server/agent/activity-curator.js +101 -24
- package/dist/server/server/agent/agent-manager.js +5 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +7 -2
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +8 -1
- package/dist/server/server/agent/provider-snapshot-manager.js +78 -33
- package/dist/server/server/agent/providers/acp-agent.d.ts +7 -0
- package/dist/server/server/agent/providers/acp-agent.js +8 -1
- package/dist/server/server/agent/providers/claude/agent.js +51 -14
- package/dist/server/server/agent/providers/claude/query.d.ts +3 -0
- package/dist/server/server/agent/providers/claude/query.js +4 -2
- package/dist/server/server/agent/providers/mock-load-test-agent.js +8 -0
- package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/paths.js +7 -0
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +34 -5
- package/dist/server/server/agent/providers/opencode-agent.d.ts +4 -0
- package/dist/server/server/agent/providers/opencode-agent.js +14 -2
- package/dist/server/server/agent/providers/pi/agent.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/agent.js +9 -3
- package/dist/server/server/agent/providers/provider-image-output.js +11 -6
- package/dist/server/server/agent/tools/paseo-tools.d.ts +1 -1
- package/dist/server/server/agent/tools/paseo-tools.js +0 -2
- package/dist/server/server/bootstrap.d.ts +7 -1
- package/dist/server/server/bootstrap.js +18 -0
- package/dist/server/server/config.d.ts +2 -0
- package/dist/server/server/config.js +57 -1
- package/dist/server/server/daemon-worker.js +19 -7
- package/dist/server/server/lifecycle-reasons.d.ts +4 -0
- package/dist/server/server/lifecycle-reasons.js +6 -0
- package/dist/server/server/persisted-config.d.ts +7 -0
- package/dist/server/server/persisted-config.js +8 -0
- package/dist/server/server/process-diagnostics.d.ts +17 -0
- package/dist/server/server/process-diagnostics.js +22 -0
- package/dist/server/server/relay-transport.js +1 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +3 -1
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
- package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
- package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
- package/dist/server/server/session/daemon/daemon-session.d.ts +12 -0
- package/dist/server/server/session/daemon/daemon-session.js +12 -0
- package/dist/server/server/session/daemon/diagnostics.js +10 -0
- package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
- package/dist/server/server/session/daemon/install-origin.js +64 -0
- package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
- package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
- package/dist/server/server/session/provider/provider-catalog-session.js +8 -4
- package/dist/server/server/session.d.ts +5 -3
- package/dist/server/server/session.js +74 -32
- package/dist/server/server/web-ui.d.ts +10 -0
- package/dist/server/server/web-ui.js +205 -0
- package/dist/server/server/websocket/runtime-metrics.d.ts +3 -0
- package/dist/server/server/websocket-server.d.ts +3 -0
- package/dist/server/server/websocket-server.js +190 -32
- package/dist/server/services/quota-fetcher/manifest.js +5 -0
- package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
- package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.js +2 -2
- package/dist/server/utils/checkout-git.js +156 -3
- package/dist/server/utils/directory-suggestions.js +1 -4
- package/dist/server/utils/path.d.ts +2 -0
- package/dist/server/utils/path.js +13 -0
- package/dist/server/utils/worktree.d.ts +1 -0
- package/dist/server/utils/worktree.js +92 -11
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-b01555c9b42665a03988c0a0032ef528.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-b01555c9b42665a03988c0a0032ef528.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-b01555c9b42665a03988c0a0032ef528.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-648388eca5c510b496e1eddf523f70ff.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-648388eca5c510b496e1eddf523f70ff.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-648388eca5c510b496e1eddf523f70ff.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-0ebbea2cd337f0c0680fdb3f8d4d5af3.js +16157 -0
- package/dist/server/web-ui/_expo/static/js/web/index-0ebbea2cd337f0c0680fdb3f8d4d5af3.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-0ebbea2cd337f0c0680fdb3f8d4d5af3.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-c64fa2416284927857a39087fd8d1332.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-c64fa2416284927857a39087fd8d1332.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-c64fa2416284927857a39087fd8d1332.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-a9784226715772edf87ef36c596599c2.js +3 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-a9784226715772edf87ef36c596599c2.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-a9784226715772edf87ef36c596599c2.js.gz +0 -0
- package/dist/server/web-ui/apple-touch-icon.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/file-explorer.3e15e8f72c825c85ce336bcb0cdef776.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/notification-icon.3bf81d33ddbf380606bdd248ba83e158.png +0 -0
- package/dist/server/web-ui/favicon.ico +0 -0
- package/dist/server/web-ui/index.html +90 -0
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/dist/server/web-ui/manifest.json +27 -0
- package/dist/server/web-ui/manifest.json.br +0 -0
- package/dist/server/web-ui/manifest.json.gz +0 -0
- package/dist/server/web-ui/metadata.json +1 -0
- package/dist/server/web-ui/metadata.json.br +1 -0
- package/dist/server/web-ui/metadata.json.gz +0 -0
- package/dist/server/web-ui/pwa-icon-192.png +0 -0
- package/dist/server/web-ui/pwa-icon-512.png +0 -0
- package/dist/server/web-ui/robots.txt +2 -0
- package/dist/src/server/persisted-config.js +8 -0
- package/package.json +7 -7
|
@@ -22,6 +22,7 @@ import { getAgentStreamEventTurnId, } from "../../agent-sdk-types.js";
|
|
|
22
22
|
import { importSessionFromPersistence } from "../../provider-session-import.js";
|
|
23
23
|
import { checkProviderLaunchAvailable, createProviderEnv, createProviderEnvSpec, resolveProviderLaunch, } from "../../provider-launch-config.js";
|
|
24
24
|
import { withTimeout } from "../../../../utils/promise-timeout.js";
|
|
25
|
+
import { terminateWithTreeKill } from "../../../../utils/tree-kill.js";
|
|
25
26
|
import { execCommand } from "../../../../utils/spawn.js";
|
|
26
27
|
import { composeSystemPromptParts } from "../../system-prompt.js";
|
|
27
28
|
const fsPromises = promises;
|
|
@@ -1289,6 +1290,13 @@ function readLegacyResultUsageTokens(usage) {
|
|
|
1289
1290
|
function isClaudeSubagentToolName(name) {
|
|
1290
1291
|
return name === "Task" || name === "Agent";
|
|
1291
1292
|
}
|
|
1293
|
+
function readClaudeParentToolUseId(message) {
|
|
1294
|
+
if (!("parent_tool_use_id" in message)) {
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
const parentToolUseId = message.parent_tool_use_id;
|
|
1298
|
+
return typeof parentToolUseId === "string" && parentToolUseId.length > 0 ? parentToolUseId : null;
|
|
1299
|
+
}
|
|
1292
1300
|
class ClaudeContextUsageState {
|
|
1293
1301
|
constructor(initialContextWindowMaxTokens) {
|
|
1294
1302
|
this.completedResultTurns = 0;
|
|
@@ -1414,6 +1422,7 @@ class ClaudeAgentSession {
|
|
|
1414
1422
|
this.provider = "claude";
|
|
1415
1423
|
this.capabilities = CLAUDE_CAPABILITIES;
|
|
1416
1424
|
this.query = null;
|
|
1425
|
+
this.childProcess = null;
|
|
1417
1426
|
this.input = null;
|
|
1418
1427
|
this.planResumeMode = null;
|
|
1419
1428
|
this.availableModes = DEFAULT_MODES;
|
|
@@ -1887,6 +1896,19 @@ class ClaudeAgentSession {
|
|
|
1887
1896
|
await this.awaitWithTimeout(this.query?.return?.(), "close query return");
|
|
1888
1897
|
this.query = null;
|
|
1889
1898
|
this.input = null;
|
|
1899
|
+
// Terminate the entire process tree (claude + MCP children) to prevent
|
|
1900
|
+
// orphan accumulation. The SDK's internal cleanup may only kill the
|
|
1901
|
+
// direct child process.
|
|
1902
|
+
if (this.childProcess) {
|
|
1903
|
+
const result = await terminateWithTreeKill(this.childProcess, {
|
|
1904
|
+
gracefulTimeoutMs: 2000,
|
|
1905
|
+
forceTimeoutMs: 2000,
|
|
1906
|
+
});
|
|
1907
|
+
if (result === "kill-timeout") {
|
|
1908
|
+
this.logger.warn({ pid: this.childProcess.pid, agentId: this.agentId }, "Claude process tree did not report exit after SIGKILL");
|
|
1909
|
+
}
|
|
1910
|
+
this.childProcess = null;
|
|
1911
|
+
}
|
|
1890
1912
|
if (this.persistSession === false && this.claudeSessionId) {
|
|
1891
1913
|
// Claude Code currently ignores --no-session-persistence outside --print mode
|
|
1892
1914
|
// (see `claude --help`), so the SDK's persistSession=false is silently dropped
|
|
@@ -2225,6 +2247,18 @@ class ClaudeAgentSession {
|
|
|
2225
2247
|
catch {
|
|
2226
2248
|
/* ignore */
|
|
2227
2249
|
}
|
|
2250
|
+
// Tree-kill the old process tree now that the SDK has cleaned up.
|
|
2251
|
+
// If we skip this, MCP children of the previous claude process can
|
|
2252
|
+
// survive as orphans when the session spawns a replacement query.
|
|
2253
|
+
if (this.childProcess) {
|
|
2254
|
+
await terminateWithTreeKill(this.childProcess, {
|
|
2255
|
+
gracefulTimeoutMs: 2000,
|
|
2256
|
+
forceTimeoutMs: 2000,
|
|
2257
|
+
}).catch(() => {
|
|
2258
|
+
/* process may already be dead */
|
|
2259
|
+
});
|
|
2260
|
+
this.childProcess = null;
|
|
2261
|
+
}
|
|
2228
2262
|
}
|
|
2229
2263
|
// Preserve claudeSessionId across query recreation so buildOptions() passes
|
|
2230
2264
|
// resume: sessionId and the new query continues the existing conversation.
|
|
@@ -2237,6 +2271,9 @@ class ClaudeAgentSession {
|
|
|
2237
2271
|
runtimeSettings: this.runtimeSettings,
|
|
2238
2272
|
launchEnv: this.launchEnv,
|
|
2239
2273
|
queryFactory: this.queryFactory,
|
|
2274
|
+
onChildProcess: (child) => {
|
|
2275
|
+
this.childProcess = child;
|
|
2276
|
+
},
|
|
2240
2277
|
});
|
|
2241
2278
|
const fastMode = this.resolveFastModeSetting();
|
|
2242
2279
|
if (fastMode !== null) {
|
|
@@ -2809,17 +2846,19 @@ class ClaudeAgentSession {
|
|
|
2809
2846
|
suppressAssistantText: true,
|
|
2810
2847
|
suppressReasoning: true,
|
|
2811
2848
|
});
|
|
2812
|
-
const assistantTimelineEvents =
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2849
|
+
const assistantTimelineEvents = readClaudeParentToolUseId(message)
|
|
2850
|
+
? []
|
|
2851
|
+
: this.timelineAssembler
|
|
2852
|
+
.consume({
|
|
2853
|
+
message,
|
|
2854
|
+
runId: turnId,
|
|
2855
|
+
messageIdHint,
|
|
2856
|
+
})
|
|
2857
|
+
.map((item) => ({
|
|
2858
|
+
type: "timeline",
|
|
2859
|
+
item,
|
|
2860
|
+
provider: "claude",
|
|
2861
|
+
}));
|
|
2823
2862
|
return [...messageEvents, ...assistantTimelineEvents];
|
|
2824
2863
|
}
|
|
2825
2864
|
async handleMissingResumedConversation(message, activeQuery) {
|
|
@@ -2867,9 +2906,7 @@ class ClaudeAgentSession {
|
|
|
2867
2906
|
}
|
|
2868
2907
|
}
|
|
2869
2908
|
translateMessageToEvents(message, options) {
|
|
2870
|
-
const parentToolUseId =
|
|
2871
|
-
? message.parent_tool_use_id
|
|
2872
|
-
: null;
|
|
2909
|
+
const parentToolUseId = readClaudeParentToolUseId(message);
|
|
2873
2910
|
if (parentToolUseId) {
|
|
2874
2911
|
return this.sidechainTracker.handleMessage(message, parentToolUseId);
|
|
2875
2912
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
1
2
|
import { query, type Options, type Query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
3
|
import { type ProviderRuntimeSettings } from "../../provider-launch-config.js";
|
|
3
4
|
export type ClaudeOptions = Options;
|
|
@@ -9,6 +10,8 @@ export interface ClaudeQueryContext {
|
|
|
9
10
|
runtimeSettings?: ProviderRuntimeSettings;
|
|
10
11
|
launchEnv?: Record<string, string>;
|
|
11
12
|
queryFactory?: ClaudeQueryFactory;
|
|
13
|
+
/** Called with the spawned child process so the caller can tree-kill it on close. */
|
|
14
|
+
onChildProcess?: (child: ChildProcess) => void;
|
|
12
15
|
}
|
|
13
16
|
export declare function claudeQuery(input: ClaudeQueryInput, context?: ClaudeQueryContext): Query;
|
|
14
17
|
//# sourceMappingURL=query.d.ts.map
|
|
@@ -24,7 +24,8 @@ function resolveClaudeSpawnCommand(spawnOptions, runtimeSettings) {
|
|
|
24
24
|
args: [...commandConfig.argv.slice(1), ...spawnOptions.args],
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
function applyRuntimeSettingsToClaudeOptions(options,
|
|
27
|
+
function applyRuntimeSettingsToClaudeOptions(options, context) {
|
|
28
|
+
const { runtimeSettings, launchEnv, onChildProcess } = context;
|
|
28
29
|
return {
|
|
29
30
|
...options,
|
|
30
31
|
spawnClaudeCodeProcess: (spawnOptions) => {
|
|
@@ -62,6 +63,7 @@ function applyRuntimeSettingsToClaudeOptions(options, runtimeSettings, launchEnv
|
|
|
62
63
|
// The command is always a resolved binary path, so shell routing is unnecessary.
|
|
63
64
|
shell: false,
|
|
64
65
|
});
|
|
66
|
+
onChildProcess?.(child);
|
|
65
67
|
if (typeof options.stderr === "function") {
|
|
66
68
|
child.stderr?.on("data", (chunk) => {
|
|
67
69
|
options.stderr?.(chunk.toString());
|
|
@@ -78,7 +80,7 @@ export function claudeQuery(input, context = {}) {
|
|
|
78
80
|
const launchQuery = context.queryFactory ?? query;
|
|
79
81
|
return launchQuery({
|
|
80
82
|
...input,
|
|
81
|
-
options: applyRuntimeSettingsToClaudeOptions(input.options, context
|
|
83
|
+
options: applyRuntimeSettingsToClaudeOptions(input.options, context),
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
//# sourceMappingURL=query.js.map
|
|
@@ -439,12 +439,14 @@ export class MockLoadTestAgentSession {
|
|
|
439
439
|
}
|
|
440
440
|
const profile = resolveModelProfile(this.modelId);
|
|
441
441
|
const turnId = randomUUID();
|
|
442
|
+
const assistantMessageId = randomUUID();
|
|
442
443
|
let resolve;
|
|
443
444
|
const completed = new Promise((promiseResolve) => {
|
|
444
445
|
resolve = promiseResolve;
|
|
445
446
|
});
|
|
446
447
|
const turn = {
|
|
447
448
|
turnId,
|
|
449
|
+
assistantMessageId,
|
|
448
450
|
prompt,
|
|
449
451
|
startedAt: Date.now(),
|
|
450
452
|
cycle: 0,
|
|
@@ -655,6 +657,7 @@ export class MockLoadTestAgentSession {
|
|
|
655
657
|
this.emitTimeline(turn.turnId, {
|
|
656
658
|
type: "assistant_message",
|
|
657
659
|
text: finalText,
|
|
660
|
+
messageId: turn.assistantMessageId,
|
|
658
661
|
});
|
|
659
662
|
this.activeTurn = null;
|
|
660
663
|
this.emit({
|
|
@@ -669,6 +672,7 @@ export class MockLoadTestAgentSession {
|
|
|
669
672
|
{
|
|
670
673
|
type: "assistant_message",
|
|
671
674
|
text: finalText,
|
|
675
|
+
messageId: turn.assistantMessageId,
|
|
672
676
|
},
|
|
673
677
|
],
|
|
674
678
|
canceled: false,
|
|
@@ -768,6 +772,7 @@ export class MockLoadTestAgentSession {
|
|
|
768
772
|
? {
|
|
769
773
|
type: "assistant_message",
|
|
770
774
|
text: `stress-update-${index}`,
|
|
775
|
+
messageId: turn.assistantMessageId,
|
|
771
776
|
}
|
|
772
777
|
: {
|
|
773
778
|
type: "todo",
|
|
@@ -834,6 +839,7 @@ export class MockLoadTestAgentSession {
|
|
|
834
839
|
this.emitTimeline(turn.turnId, {
|
|
835
840
|
type: "assistant_message",
|
|
836
841
|
text: `data:image/png;base64,${payload}`,
|
|
842
|
+
messageId: turn.assistantMessageId,
|
|
837
843
|
});
|
|
838
844
|
}
|
|
839
845
|
this.activeTurn = null;
|
|
@@ -892,6 +898,7 @@ export class MockLoadTestAgentSession {
|
|
|
892
898
|
this.emitTimeline(turn.turnId, {
|
|
893
899
|
type: "assistant_message",
|
|
894
900
|
text: event.text,
|
|
901
|
+
messageId: turn.assistantMessageId,
|
|
895
902
|
});
|
|
896
903
|
return;
|
|
897
904
|
}
|
|
@@ -933,6 +940,7 @@ export class MockLoadTestAgentSession {
|
|
|
933
940
|
this.emitTimeline(turn.turnId, {
|
|
934
941
|
type: "assistant_message",
|
|
935
942
|
text: "\n\n_(end of synthetic stream)_\n",
|
|
943
|
+
messageId: turn.assistantMessageId,
|
|
936
944
|
});
|
|
937
945
|
this.finishTurnWithText(turn, "Synthetic load test complete");
|
|
938
946
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { resolvePaseoHome } from "../../../paseo-home.js";
|
|
3
|
+
const OPENCODE_HOME_DIRNAME = "opencode-home";
|
|
4
|
+
export function resolveOpenCodeHomeDir(env = process.env) {
|
|
5
|
+
return path.join(resolvePaseoHome(env), OPENCODE_HOME_DIRNAME);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -46,6 +46,7 @@ export interface OpenCodeServerManagerOptions {
|
|
|
46
46
|
terminateProcess?: ProcessTerminator;
|
|
47
47
|
portAllocator?: OpenCodePortAllocator;
|
|
48
48
|
resolveCommandPrefix?: OpenCodeCommandPrefixResolver;
|
|
49
|
+
resolveHomeDir?: () => string;
|
|
49
50
|
spawnServerProcess?: OpenCodeServerProcessSpawner;
|
|
50
51
|
}
|
|
51
52
|
export declare class OpenCodeServerManager implements OpenCodeServerManagerLike {
|
|
@@ -62,6 +63,7 @@ export declare class OpenCodeServerManager implements OpenCodeServerManagerLike
|
|
|
62
63
|
private readonly terminateProcess;
|
|
63
64
|
private readonly portAllocator;
|
|
64
65
|
private readonly resolveCommandPrefix;
|
|
66
|
+
private readonly resolveHomeDir;
|
|
65
67
|
private readonly spawnServerProcess;
|
|
66
68
|
constructor(options: OpenCodeServerManagerOptions);
|
|
67
69
|
static getInstance(logger: Logger, runtimeSettings?: ProviderRuntimeSettings, options?: Omit<OpenCodeServerManagerOptions, "logger" | "runtimeSettings">): OpenCodeServerManager;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
1
3
|
import net from "node:net";
|
|
2
|
-
import
|
|
4
|
+
import path from "node:path";
|
|
3
5
|
import { findExecutable } from "../../../../executable-resolution/executable-resolution.js";
|
|
4
6
|
import { spawnProcess } from "../../../../utils/spawn.js";
|
|
5
7
|
import { terminateWithTreeKill } from "../../../../utils/tree-kill.js";
|
|
6
8
|
import { createProviderEnvSpec, resolveProviderCommandPrefix, } from "../../provider-launch-config.js";
|
|
9
|
+
import { resolveOpenCodeHomeDir } from "./paths.js";
|
|
7
10
|
const OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
8
11
|
const OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS = 1000;
|
|
9
12
|
export class OpenCodeServerManager {
|
|
@@ -21,6 +24,7 @@ export class OpenCodeServerManager {
|
|
|
21
24
|
this.resolveCommandPrefix =
|
|
22
25
|
options.resolveCommandPrefix ??
|
|
23
26
|
(() => resolveProviderCommandPrefix(this.runtimeSettings?.command, resolveOpenCodeBinary));
|
|
27
|
+
this.resolveHomeDir = options.resolveHomeDir ?? resolveOpenCodeHomeDir;
|
|
24
28
|
this.spawnServerProcess = options.spawnServerProcess ?? spawnProcess;
|
|
25
29
|
}
|
|
26
30
|
static getInstance(logger, runtimeSettings, options = {}) {
|
|
@@ -164,7 +168,11 @@ export class OpenCodeServerManager {
|
|
|
164
168
|
const url = `http://127.0.0.1:${port}`;
|
|
165
169
|
const launchPrefix = await this.resolveCommandPrefix();
|
|
166
170
|
const serverArgs = [...launchPrefix.args, "serve", "--port", String(port)];
|
|
167
|
-
|
|
171
|
+
// Use a neutral OpenCode home as the server cwd. Launching from the user's
|
|
172
|
+
// home directory causes OpenCode to treat it as the default workspace and
|
|
173
|
+
// index the entire home tree.
|
|
174
|
+
const serverCwd = this.resolveHomeDir();
|
|
175
|
+
mkdirSync(serverCwd, { recursive: true });
|
|
168
176
|
const serverProcess = this.spawnServerProcess(launchPrefix.command, serverArgs, {
|
|
169
177
|
cwd: serverCwd,
|
|
170
178
|
detached: process.platform !== "win32",
|
|
@@ -371,10 +379,31 @@ OpenCodeServerManager.instance = null;
|
|
|
371
379
|
OpenCodeServerManager.exitHandlerRegistered = false;
|
|
372
380
|
async function resolveOpenCodeBinary() {
|
|
373
381
|
const found = await findExecutable("opencode");
|
|
374
|
-
if (found) {
|
|
375
|
-
|
|
382
|
+
if (!found) {
|
|
383
|
+
throw new Error("OpenCode binary not found. Install OpenCode (https://github.com/opencode-ai/opencode) and ensure it is available in your shell PATH.");
|
|
384
|
+
}
|
|
385
|
+
if (process.platform === "win32" && path.extname(found).toLowerCase() === ".cmd") {
|
|
386
|
+
// Global npm: <prefix>/opencode.cmd → <prefix>/node_modules/opencode-ai/bin/opencode.exe
|
|
387
|
+
const globalCandidate = path.join(path.dirname(found), "node_modules", "opencode-ai", "bin", "opencode.exe");
|
|
388
|
+
if (await pathExists(globalCandidate))
|
|
389
|
+
return globalCandidate;
|
|
390
|
+
// Local/pnpm: <project>/node_modules/.bin/opencode.cmd → <project>/node_modules/opencode-ai/bin/opencode.exe
|
|
391
|
+
const localCandidate = path.join(path.dirname(found), "..", "opencode-ai", "bin", "opencode.exe");
|
|
392
|
+
if (await pathExists(localCandidate))
|
|
393
|
+
return localCandidate;
|
|
394
|
+
console.warn("[opencode-server] Found opencode.cmd but could not resolve the real opencode.exe. " +
|
|
395
|
+
"The process may not be properly terminated on exit. Path: %s", found);
|
|
396
|
+
}
|
|
397
|
+
return found;
|
|
398
|
+
}
|
|
399
|
+
async function pathExists(filePath) {
|
|
400
|
+
try {
|
|
401
|
+
await stat(filePath);
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
return false;
|
|
376
406
|
}
|
|
377
|
-
throw new Error("OpenCode binary not found. Install OpenCode (https://github.com/opencode-ai/opencode) and ensure it is available in your shell PATH.");
|
|
378
407
|
}
|
|
379
408
|
function findAvailablePort() {
|
|
380
409
|
return new Promise((resolve, reject) => {
|
|
@@ -4,6 +4,7 @@ import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOpt
|
|
|
4
4
|
import { isDefaultAgentCreateConfigUnattended } from "../create-agent-mode.js";
|
|
5
5
|
import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
|
|
6
6
|
import { type OpenCodeServerManagerLike } from "./opencode/server-manager.js";
|
|
7
|
+
import { resolveOpenCodeHomeDir } from "./opencode/paths.js";
|
|
7
8
|
import type { ManagedProcessRegistry } from "../../managed-processes/managed-processes.js";
|
|
8
9
|
declare function resolveOpenCodeCreateConfig(input: ResolveAgentCreateConfigInput): ResolveAgentCreateConfigResult;
|
|
9
10
|
declare function isOpenCodeCreateConfigUnattended(input: Parameters<typeof isDefaultAgentCreateConfigUnattended>[0]): boolean;
|
|
@@ -100,11 +101,13 @@ export declare const __openCodeInternals: {
|
|
|
100
101
|
resolveOpenCodeSelectedModelContextWindow: typeof resolveOpenCodeSelectedModelContextWindow;
|
|
101
102
|
isSelectableOpenCodeAgent: typeof isSelectableOpenCodeAgent;
|
|
102
103
|
mapOpenCodeAgentToMode: typeof mapOpenCodeAgentToMode;
|
|
104
|
+
resolveOpenCodeHomeDir: typeof resolveOpenCodeHomeDir;
|
|
103
105
|
readonly OpenCodeAgentSession: typeof OpenCodeAgentSession;
|
|
104
106
|
};
|
|
105
107
|
interface OpenCodeAgentClientDeps {
|
|
106
108
|
serverManager?: OpenCodeServerManagerLike;
|
|
107
109
|
createClient?: OpenCodeClientFactory;
|
|
110
|
+
resolveHomeDir?: () => string;
|
|
108
111
|
managedProcesses?: ManagedProcessRegistry;
|
|
109
112
|
}
|
|
110
113
|
type OpenCodeClientFactory = (options: {
|
|
@@ -118,6 +121,7 @@ export declare class OpenCodeAgentClient implements AgentClient {
|
|
|
118
121
|
readonly isCreateConfigUnattended: typeof isOpenCodeCreateConfigUnattended;
|
|
119
122
|
private readonly serverManager;
|
|
120
123
|
private readonly createOpenCodeClient;
|
|
124
|
+
private readonly resolveHomeDir;
|
|
121
125
|
private readonly logger;
|
|
122
126
|
private readonly runtimeSettings?;
|
|
123
127
|
private readonly modelContextWindows;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createOpencodeClient, } from "@opencode-ai/sdk/v2/client";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
2
3
|
import { createPathEquivalenceMatcher } from "../../../utils/path.js";
|
|
3
4
|
import pLimit from "p-limit";
|
|
4
5
|
import { z } from "zod";
|
|
@@ -11,6 +12,7 @@ import { execCommand } from "../../../utils/spawn.js";
|
|
|
11
12
|
import { buildToolCallDisplayModel } from "@getpaseo/protocol/tool-call-display";
|
|
12
13
|
import { mapOpencodeToolCall } from "./opencode/tool-call-mapper.js";
|
|
13
14
|
import { OpenCodeServerManager, } from "./opencode/server-manager.js";
|
|
15
|
+
import { resolveOpenCodeHomeDir } from "./opencode/paths.js";
|
|
14
16
|
import { formatProviderDiagnostic, formatProviderDiagnosticError, buildBinaryDiagnosticRows, buildCommandResolutionDiagnosticRows, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
|
|
15
17
|
import { runProviderTurn } from "./provider-runner.js";
|
|
16
18
|
import { renderPromptAttachmentAsText } from "../prompt-attachments.js";
|
|
@@ -847,6 +849,7 @@ export const __openCodeInternals = {
|
|
|
847
849
|
resolveOpenCodeSelectedModelContextWindow,
|
|
848
850
|
isSelectableOpenCodeAgent,
|
|
849
851
|
mapOpenCodeAgentToMode,
|
|
852
|
+
resolveOpenCodeHomeDir,
|
|
850
853
|
get OpenCodeAgentSession() {
|
|
851
854
|
return OpenCodeAgentSession;
|
|
852
855
|
},
|
|
@@ -867,8 +870,10 @@ export class OpenCodeAgentClient {
|
|
|
867
870
|
deps.serverManager ??
|
|
868
871
|
OpenCodeServerManager.getInstance(this.logger, runtimeSettings, {
|
|
869
872
|
managedProcesses: deps.managedProcesses,
|
|
873
|
+
resolveHomeDir: deps.resolveHomeDir,
|
|
870
874
|
});
|
|
871
875
|
this.createOpenCodeClient = deps.createClient ?? createSdkOpenCodeClient;
|
|
876
|
+
this.resolveHomeDir = deps.resolveHomeDir ?? resolveOpenCodeHomeDir;
|
|
872
877
|
}
|
|
873
878
|
async createSession(config, launchContext, options) {
|
|
874
879
|
const openCodeConfig = this.assertConfig(config);
|
|
@@ -930,9 +935,16 @@ export class OpenCodeAgentClient {
|
|
|
930
935
|
? await this.serverManager.acquireNew()
|
|
931
936
|
: await this.serverManager.acquireCurrent();
|
|
932
937
|
const { url } = acquisition.server;
|
|
933
|
-
const
|
|
934
|
-
const client = this.createOpenCodeClient({ baseUrl: url, directory });
|
|
938
|
+
const isGlobalCatalog = options.scope === "global";
|
|
935
939
|
try {
|
|
940
|
+
// OpenCode treats the catalog directory as a workspace. The global catalog
|
|
941
|
+
// is not a project, so use the neutral OpenCode home instead of user home.
|
|
942
|
+
const directory = isGlobalCatalog ? this.resolveHomeDir() : options.cwd;
|
|
943
|
+
if (isGlobalCatalog) {
|
|
944
|
+
await fs.mkdir(directory, { recursive: true });
|
|
945
|
+
this.logger.debug({ directory }, "opencode catalog refresh: using opencode-home for global provider catalog");
|
|
946
|
+
}
|
|
947
|
+
const client = this.createOpenCodeClient({ baseUrl: url, directory });
|
|
936
948
|
const [models, modes] = await Promise.all([
|
|
937
949
|
this.fetchModelsFromClient(client, directory),
|
|
938
950
|
this.fetchModesFromClient(client, directory),
|
|
@@ -6,6 +6,7 @@ import type { PiRuntime, PiRuntimeSession } from "./runtime.js";
|
|
|
6
6
|
import type { PiCommandsRpcType, PiSessionState } from "./rpc-types.js";
|
|
7
7
|
export declare const PiProviderParamsSchema: z.ZodObject<{
|
|
8
8
|
sessionDir: z.ZodOptional<z.ZodString>;
|
|
9
|
+
extensionTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
9
10
|
}, z.core.$strict>;
|
|
10
11
|
interface PiRpcAgentClientOptions {
|
|
11
12
|
logger: Logger;
|
|
@@ -23,6 +24,7 @@ interface PiRpcAgentSessionOptions {
|
|
|
23
24
|
initialState: PiSessionState;
|
|
24
25
|
capabilities: AgentCapabilityFlags;
|
|
25
26
|
cleanup?: () => void;
|
|
27
|
+
extensionTimeoutMs?: number;
|
|
26
28
|
}
|
|
27
29
|
export declare function transformPiModels(models: AgentModelDefinition[]): AgentModelDefinition[];
|
|
28
30
|
export declare class PiRpcAgentSession implements AgentSession {
|
|
@@ -50,6 +52,7 @@ export declare class PiRpcAgentSession implements AgentSession {
|
|
|
50
52
|
private readonly runtimeSession;
|
|
51
53
|
private readonly config;
|
|
52
54
|
private readonly cleanup?;
|
|
55
|
+
private readonly extensionTimeoutMs;
|
|
53
56
|
get id(): string | null;
|
|
54
57
|
run(prompt: AgentPromptInput, options?: AgentRunOptions): Promise<AgentRunResult>;
|
|
55
58
|
startTurn(prompt: AgentPromptInput, _options?: AgentRunOptions): Promise<StartTurnResult>;
|
|
@@ -22,7 +22,7 @@ const PASEO_PI_TREE_EXTENSION_COMMAND = "paseo_tree";
|
|
|
22
22
|
const PASEO_PI_CAPTURE_EXTENSION_COMMAND = "paseo_capture_entries";
|
|
23
23
|
const PASEO_PI_ENTRY_CAPTURE_MARKER = "PASEO_ENTRY_CAPTURE";
|
|
24
24
|
const PASEO_PI_COMMAND_RESULT_MARKER = "PASEO_COMMAND_RESULT";
|
|
25
|
-
const
|
|
25
|
+
const DEFAULT_PI_EXTENSION_RESULT_TIMEOUT_MS = 30000;
|
|
26
26
|
const QUESTION_RESPONSE_HEADER = "Response";
|
|
27
27
|
const QUESTION_COMMENT_HEADER = "Comment";
|
|
28
28
|
const PI_ASK_USER_FREEFORM_SENTINEL = "✏️ Type custom response...";
|
|
@@ -30,6 +30,7 @@ const COMBINED_ASK_USER_METADATA = "ask_user_select_optional_comment";
|
|
|
30
30
|
export const PiProviderParamsSchema = z
|
|
31
31
|
.object({
|
|
32
32
|
sessionDir: z.string().min(1).optional(),
|
|
33
|
+
extensionTimeoutMs: z.number().int().positive().default(DEFAULT_PI_EXTENSION_RESULT_TIMEOUT_MS),
|
|
33
34
|
})
|
|
34
35
|
.strict();
|
|
35
36
|
const PI_HANDLED_BUILTIN_SLASH_COMMANDS = [
|
|
@@ -715,6 +716,7 @@ export class PiRpcAgentSession {
|
|
|
715
716
|
normalizePiThinkingOption(options.config.thinkingOptionId) ??
|
|
716
717
|
this.state.thinkingLevel ??
|
|
717
718
|
null;
|
|
719
|
+
this.extensionTimeoutMs = options.extensionTimeoutMs ?? DEFAULT_PI_EXTENSION_RESULT_TIMEOUT_MS;
|
|
718
720
|
this.runtimeSession.onEvent((event) => {
|
|
719
721
|
this.handleRuntimeEvent(event);
|
|
720
722
|
});
|
|
@@ -1065,7 +1067,7 @@ export class PiRpcAgentSession {
|
|
|
1065
1067
|
const timer = setTimeout(() => {
|
|
1066
1068
|
this.pendingExtensionResults.delete(requestId);
|
|
1067
1069
|
reject(new Error(`Pi extension result timed out for request ${requestId}`));
|
|
1068
|
-
},
|
|
1070
|
+
}, this.extensionTimeoutMs);
|
|
1069
1071
|
this.pendingExtensionResults.set(requestId, { resolve, reject, timer });
|
|
1070
1072
|
});
|
|
1071
1073
|
}
|
|
@@ -1489,6 +1491,7 @@ export class PiRpcAgentClient {
|
|
|
1489
1491
|
initialState: await runtimeSession.getState(),
|
|
1490
1492
|
capabilities: withPiMcpCapability(mcpConfig !== null),
|
|
1491
1493
|
cleanup: combineCleanup([mcpConfig?.cleanup, paseoExtension.cleanup]),
|
|
1494
|
+
extensionTimeoutMs: this.providerParams.extensionTimeoutMs,
|
|
1492
1495
|
});
|
|
1493
1496
|
}
|
|
1494
1497
|
catch (error) {
|
|
@@ -1531,6 +1534,7 @@ export class PiRpcAgentClient {
|
|
|
1531
1534
|
initialState: await runtimeSession.getState(),
|
|
1532
1535
|
capabilities: withPiMcpCapability(mcpConfig !== null),
|
|
1533
1536
|
cleanup: combineCleanup([mcpConfig?.cleanup, paseoExtension.cleanup]),
|
|
1537
|
+
extensionTimeoutMs: this.providerParams.extensionTimeoutMs,
|
|
1534
1538
|
});
|
|
1535
1539
|
}
|
|
1536
1540
|
catch (error) {
|
|
@@ -1541,7 +1545,9 @@ export class PiRpcAgentClient {
|
|
|
1541
1545
|
}
|
|
1542
1546
|
}
|
|
1543
1547
|
async fetchCatalog(options) {
|
|
1544
|
-
const runtimeSession = await this.runtime.startSession({
|
|
1548
|
+
const runtimeSession = await this.runtime.startSession({
|
|
1549
|
+
cwd: options.scope === "global" ? homedir() : options.cwd,
|
|
1550
|
+
});
|
|
1545
1551
|
try {
|
|
1546
1552
|
const models = transformPiModels((await runtimeSession.getAvailableModels(PI_CATALOG_REQUEST_TIMEOUT_MS)).map(mapPiModel));
|
|
1547
1553
|
return { models, modes: [] };
|
|
@@ -44,11 +44,10 @@ export function materializeProviderImage(image) {
|
|
|
44
44
|
fsSync.writeFileSync(filePath, bytes);
|
|
45
45
|
return { path: filePath };
|
|
46
46
|
}
|
|
47
|
-
// Recognizes
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// because on Windows the path uses "\\" and escapeMarkdownImageSource doubles each backslash.
|
|
47
|
+
// Recognizes markdown rendered for a materialized provider image: its source is a content-hashed
|
|
48
|
+
// file in the attachments dir. Matching the full <hash>.<ext> shape (not just a leading "![")
|
|
49
|
+
// keeps user-authored text from being mistaken for a provider image during history replay. The
|
|
50
|
+
// separator still accepts old doubled-backslash Windows history; new Windows output uses file URIs.
|
|
52
51
|
const PROVIDER_IMAGE_MARKDOWN = new RegExp(`^!\\[[^\\]]*\\]\\([^)]*${PROVIDER_IMAGE_ATTACHMENT_DIR}[/\\\\]+[0-9a-f]{64}\\.[a-z0-9]+\\)`);
|
|
53
52
|
export function isProviderImageMarkdown(text) {
|
|
54
53
|
return PROVIDER_IMAGE_MARKDOWN.test(text);
|
|
@@ -63,8 +62,14 @@ function isDataImageSource(source) {
|
|
|
63
62
|
function escapeMarkdownImageAlt(value) {
|
|
64
63
|
return value.replace(/\\/g, "\\\\").replace(/\]/g, "\\]");
|
|
65
64
|
}
|
|
65
|
+
function markdownImageSource(value) {
|
|
66
|
+
if (/^[A-Za-z]:[\\/]/.test(value)) {
|
|
67
|
+
return `file:///${value.replace(/\\/g, "/")}`;
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
66
71
|
function escapeMarkdownImageSource(value) {
|
|
67
|
-
return value.replace(/\\/g, "\\\\").replace(/\)/g, "\\)");
|
|
72
|
+
return markdownImageSource(value).replace(/\\/g, "\\\\").replace(/\)/g, "\\)");
|
|
68
73
|
}
|
|
69
74
|
export function renderProviderImageOutputAsAssistantMarkdown(image, options = {}) {
|
|
70
75
|
const source = nonEmptyString(image.path) ?? nonEmptyString(image.url);
|
|
@@ -6,7 +6,7 @@ import type { VoiceCallerContext, VoiceSpeakHandler } from "../../voice-types.js
|
|
|
6
6
|
import type { TerminalManager } from "../../../terminal/terminal-manager.js";
|
|
7
7
|
import type { CreatePaseoWorktreeWorkflowFn } from "../../worktree-session.js";
|
|
8
8
|
import type { ScheduleService } from "../../schedule/service.js";
|
|
9
|
-
import {
|
|
9
|
+
import type { ProviderSnapshotManager } from "../provider-snapshot-manager.js";
|
|
10
10
|
import type { GitHubService } from "../../../services/github-service.js";
|
|
11
11
|
import type { WorkspaceGitService } from "../../workspace-git-service.js";
|
|
12
12
|
import type { PaseoToolCatalog } from "./types.js";
|
|
@@ -11,7 +11,6 @@ import { WaitForAgentTracker } from "../wait-for-agent-tracker.js";
|
|
|
11
11
|
import { createAgentCommand } from "../create-agent/create.js";
|
|
12
12
|
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../../path-utils.js";
|
|
13
13
|
import { ScheduleRunSchema, ScheduleSummarySchema, StoredScheduleSchema, } from "@getpaseo/protocol/schedule/types";
|
|
14
|
-
import { resolveSnapshotCwd } from "../provider-snapshot-manager.js";
|
|
15
14
|
import { AgentModelSchema, AgentProviderEnum, AgentStatusEnum, ProviderModeSchema, ProviderSummarySchema, parseDurationString, resolveRequiredProviderModel, sanitizePermissionRequest, serializeSnapshotWithMetadata, toScheduleSummary, waitForAgentWithTimeout, } from "../mcp-shared.js";
|
|
16
15
|
import { sendPromptToAgent, setupFinishNotification } from "../agent-prompt.js";
|
|
17
16
|
import { respondToAgentPermission } from "../permission-response.js";
|
|
@@ -1760,7 +1759,6 @@ export function createPaseoToolCatalog(options) {
|
|
|
1760
1759
|
},
|
|
1761
1760
|
}, async ({ provider }) => {
|
|
1762
1761
|
const models = await providerSnapshotManager.listModels({
|
|
1763
|
-
cwd: resolveSnapshotCwd(),
|
|
1764
1762
|
provider,
|
|
1765
1763
|
wait: true,
|
|
1766
1764
|
});
|
|
@@ -44,11 +44,12 @@ export type DaemonLifecycleIntent = {
|
|
|
44
44
|
type: "shutdown";
|
|
45
45
|
clientId: string;
|
|
46
46
|
requestId: string;
|
|
47
|
+
reason: string;
|
|
47
48
|
} | {
|
|
48
49
|
type: "restart";
|
|
49
50
|
clientId: string;
|
|
50
51
|
requestId: string;
|
|
51
|
-
reason
|
|
52
|
+
reason: string;
|
|
52
53
|
};
|
|
53
54
|
export interface PaseoDaemonConfig {
|
|
54
55
|
listen: string;
|
|
@@ -57,6 +58,7 @@ export interface PaseoDaemonConfig {
|
|
|
57
58
|
corsAllowedOrigins: string[];
|
|
58
59
|
allowedHosts?: HostnamesConfig;
|
|
59
60
|
hostnames?: HostnamesConfig;
|
|
61
|
+
trustedProxies?: true | string[];
|
|
60
62
|
mcpEnabled?: boolean;
|
|
61
63
|
mcpInjectIntoAgents?: boolean;
|
|
62
64
|
autoArchiveAfterMerge?: boolean;
|
|
@@ -77,6 +79,10 @@ export interface PaseoDaemonConfig {
|
|
|
77
79
|
publicBaseUrl: string | null;
|
|
78
80
|
standaloneListen: string | null;
|
|
79
81
|
};
|
|
82
|
+
webUi?: {
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
distDir: string | null;
|
|
85
|
+
};
|
|
80
86
|
appBaseUrl?: string;
|
|
81
87
|
auth?: DaemonAuthConfig;
|
|
82
88
|
openai?: PaseoOpenAIConfig;
|
|
@@ -113,6 +113,7 @@ import { createManagedProcessRegistry, createSystemManagedProcessTable, } from "
|
|
|
113
113
|
import { terminateWithTreeKill } from "../utils/tree-kill.js";
|
|
114
114
|
import { isHostnameAllowed } from "./hostnames.js";
|
|
115
115
|
import { createRequireBearerMiddleware, isAgentMcpRequestAuthorized, } from "./auth.js";
|
|
116
|
+
import { createWebUiMiddleware } from "./web-ui.js";
|
|
116
117
|
const MAX_MCP_DEBUG_BATCH_ITEMS = 10;
|
|
117
118
|
const REDACTED_LOG_VALUE = "[redacted]";
|
|
118
119
|
const DOWNLOAD_OPEN_FLAGS = process.platform === "win32" ? constants.O_RDONLY : constants.O_RDONLY | constants.O_NOFOLLOW;
|
|
@@ -230,6 +231,17 @@ async function reconcileManagedProcessLedger(managedProcesses, logger) {
|
|
|
230
231
|
logger.info(reapResult, "Managed helper process ledger reconciled");
|
|
231
232
|
}
|
|
232
233
|
}
|
|
234
|
+
function mountWebUi(app, config, logger) {
|
|
235
|
+
app.use(createWebUiMiddleware({
|
|
236
|
+
enabled: config.webUi?.enabled ?? false,
|
|
237
|
+
distDir: config.webUi?.distDir ?? null,
|
|
238
|
+
label: getHostname(),
|
|
239
|
+
logger,
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
function resolveExpressTrustProxySetting(config) {
|
|
243
|
+
return config.trustedProxies ?? ["loopback"];
|
|
244
|
+
}
|
|
233
245
|
export async function createPaseoDaemon(config, rootLogger) {
|
|
234
246
|
const logger = rootLogger.child({ module: "bootstrap" });
|
|
235
247
|
const bootstrapStart = performance.now();
|
|
@@ -279,6 +291,7 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
279
291
|
const agentMcpAuthToken = randomUUID();
|
|
280
292
|
const listenTarget = parseListenString(config.listen);
|
|
281
293
|
const app = express();
|
|
294
|
+
app.set("trust proxy", resolveExpressTrustProxySetting(config));
|
|
282
295
|
let boundListenTarget = null;
|
|
283
296
|
let workspaceRegistry = null;
|
|
284
297
|
const terminalManager = createConfiguredTerminalManager({
|
|
@@ -363,6 +376,11 @@ export async function createPaseoDaemon(config, rootLogger) {
|
|
|
363
376
|
});
|
|
364
377
|
// Local, harmless, and token-gated; deliberately skips daemon auth.
|
|
365
378
|
app.post("/api/terminal-activity", express.json(), createTerminalActivityRouteHandler(terminalManager));
|
|
379
|
+
// Serve the bundled browser web UI when enabled. Mounted after service-proxy
|
|
380
|
+
// classification and host/CORS handling, but before daemon bearer auth, so
|
|
381
|
+
// static app files load without the daemon password while API/WebSocket calls
|
|
382
|
+
// remain protected.
|
|
383
|
+
mountWebUi(app, config, logger);
|
|
366
384
|
app.use(createRequireBearerMiddleware(config.auth, (context) => {
|
|
367
385
|
logger.warn(context, "Rejected HTTP request with invalid daemon password");
|
|
368
386
|
}));
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { PaseoDaemonConfig } from "./bootstrap.js";
|
|
2
2
|
import { type HostnamesConfig } from "./hostnames.js";
|
|
3
|
+
export declare function resolveBundledWebUiDistDir(moduleUrl?: string | URL): string;
|
|
3
4
|
export type CliConfigOverrides = Partial<{
|
|
4
5
|
listen: string;
|
|
5
6
|
relayEnabled: boolean;
|
|
6
7
|
relayUseTls: boolean;
|
|
7
8
|
mcpEnabled: boolean;
|
|
8
9
|
mcpInjectIntoAgents: boolean;
|
|
10
|
+
webUiEnabled: boolean;
|
|
9
11
|
hostnames: HostnamesConfig;
|
|
10
12
|
}>;
|
|
11
13
|
export declare function loadConfig(paseoHome: string, options?: {
|