@getpaseo/server 0.1.70 → 0.1.72
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/client/daemon-client.d.ts +24 -4
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +41 -2
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +17 -4
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +177 -21
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.d.ts +2 -0
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.js +16 -7
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
- package/dist/server/server/agent/agent-projections.d.ts +6 -2
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +32 -0
- package/dist/server/server/agent/agent-projections.js.map +1 -1
- package/dist/server/server/agent/agent-prompt.d.ts +72 -0
- package/dist/server/server/agent/agent-prompt.d.ts.map +1 -0
- package/dist/server/server/agent/agent-prompt.js +169 -0
- package/dist/server/server/agent/agent-prompt.js.map +1 -0
- package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -1
- package/dist/server/server/agent/agent-response-loop.js +2 -1
- package/dist/server/server/agent/agent-response-loop.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +12 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/create-agent-mode.d.ts +2 -0
- package/dist/server/server/agent/create-agent-mode.d.ts.map +1 -1
- package/dist/server/server/agent/create-agent-mode.js +3 -0
- package/dist/server/server/agent/create-agent-mode.js.map +1 -1
- package/dist/server/server/agent/create-agent-title.d.ts +8 -0
- package/dist/server/server/agent/create-agent-title.d.ts.map +1 -0
- package/dist/server/server/agent/create-agent-title.js +29 -0
- package/dist/server/server/agent/create-agent-title.js.map +1 -0
- package/dist/server/server/agent/import-sessions.d.ts +52 -0
- package/dist/server/server/agent/import-sessions.d.ts.map +1 -0
- package/dist/server/server/agent/import-sessions.js +208 -0
- package/dist/server/server/agent/import-sessions.js.map +1 -0
- package/dist/server/server/agent/mcp-server.d.ts +1 -1
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +29 -6
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/mcp-shared.d.ts +1 -47
- package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-shared.js +0 -145
- package/dist/server/server/agent/mcp-shared.js.map +1 -1
- package/dist/server/server/agent/provider-registry.d.ts +7 -0
- package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
- package/dist/server/server/agent/provider-registry.js +7 -1
- package/dist/server/server/agent/provider-registry.js.map +1 -1
- package/dist/server/server/agent/providers/claude/agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/agent.js +15 -0
- package/dist/server/server/agent/providers/claude/agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +25 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts.map +1 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.js +183 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.js.map +1 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +9 -22
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +39 -171
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode/runtime.d.ts +27 -0
- package/dist/server/server/agent/providers/opencode/runtime.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode/runtime.js +5 -0
- package/dist/server/server/agent/providers/opencode/runtime.js.map +1 -0
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +55 -0
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +255 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js.map +1 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +22 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.js +28 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.js.map +1 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +75 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +169 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js.map +1 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +7 -36
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +199 -261
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts +8 -3
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.js +44 -34
- package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -1
- package/dist/server/server/chat/chat-mentions.d.ts +24 -8
- package/dist/server/server/chat/chat-mentions.d.ts.map +1 -1
- package/dist/server/server/chat/chat-mentions.js +77 -35
- package/dist/server/server/chat/chat-mentions.js.map +1 -1
- package/dist/server/server/chat/chat-service.d.ts +4 -0
- package/dist/server/server/chat/chat-service.d.ts.map +1 -1
- package/dist/server/server/chat/chat-service.js +9 -0
- package/dist/server/server/chat/chat-service.js.map +1 -1
- package/dist/server/server/checkout/status-projection.d.ts +19 -0
- package/dist/server/server/checkout/status-projection.d.ts.map +1 -0
- package/dist/server/server/checkout/status-projection.js +98 -0
- package/dist/server/server/checkout/status-projection.js.map +1 -0
- package/dist/server/server/daemon-keypair.d.ts.map +1 -1
- package/dist/server/server/daemon-keypair.js +4 -2
- package/dist/server/server/daemon-keypair.js.map +1 -1
- package/dist/server/server/exports.d.ts +1 -0
- package/dist/server/server/exports.d.ts.map +1 -1
- package/dist/server/server/exports.js +1 -0
- package/dist/server/server/exports.js.map +1 -1
- package/dist/server/server/paseo-home.js +2 -2
- package/dist/server/server/paseo-home.js.map +1 -1
- package/dist/server/server/paseo-worktree-service.d.ts +2 -1
- package/dist/server/server/paseo-worktree-service.d.ts.map +1 -1
- package/dist/server/server/paseo-worktree-service.js +30 -3
- package/dist/server/server/paseo-worktree-service.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- package/dist/server/server/persisted-config.js +5 -4
- package/dist/server/server/persisted-config.js.map +1 -1
- package/dist/server/server/private-files.d.ts +7 -0
- package/dist/server/server/private-files.d.ts.map +1 -0
- package/dist/server/server/private-files.js +42 -0
- package/dist/server/server/private-files.js.map +1 -0
- package/dist/server/server/push/token-store.d.ts.map +1 -1
- package/dist/server/server/push/token-store.js +4 -6
- package/dist/server/server/push/token-store.js.map +1 -1
- package/dist/server/server/schedule/service.d.ts +1 -1
- package/dist/server/server/schedule/service.d.ts.map +1 -1
- package/dist/server/server/schedule/service.js +14 -11
- package/dist/server/server/schedule/service.js.map +1 -1
- package/dist/server/server/server-id.d.ts.map +1 -1
- package/dist/server/server/server-id.js +8 -3
- package/dist/server/server/server-id.js.map +1 -1
- package/dist/server/server/session.d.ts +3 -17
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +174 -254
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/workspace-directory.d.ts +0 -2
- package/dist/server/server/workspace-directory.d.ts.map +1 -1
- package/dist/server/server/workspace-directory.js +9 -26
- package/dist/server/server/workspace-directory.js.map +1 -1
- package/dist/server/server/workspace-git-service.d.ts +2 -1
- package/dist/server/server/workspace-git-service.d.ts.map +1 -1
- package/dist/server/server/workspace-git-service.js +1 -1
- package/dist/server/server/workspace-git-service.js.map +1 -1
- package/dist/server/server/worktree-branch-name-generator.d.ts +2 -0
- package/dist/server/server/worktree-branch-name-generator.d.ts.map +1 -1
- package/dist/server/server/worktree-branch-name-generator.js +18 -11
- package/dist/server/server/worktree-branch-name-generator.js.map +1 -1
- package/dist/server/services/github-service.d.ts +13 -0
- package/dist/server/services/github-service.d.ts.map +1 -1
- package/dist/server/services/github-service.js +12 -2
- package/dist/server/services/github-service.js.map +1 -1
- package/dist/server/shared/agent-labels.d.ts +2 -0
- package/dist/server/shared/agent-labels.d.ts.map +1 -0
- package/dist/server/shared/agent-labels.js +2 -0
- package/dist/server/shared/agent-labels.js.map +1 -0
- package/dist/server/shared/agent-state-bucket.d.ts +13 -0
- package/dist/server/shared/agent-state-bucket.d.ts.map +1 -0
- package/dist/server/shared/agent-state-bucket.js +41 -0
- package/dist/server/shared/agent-state-bucket.js.map +1 -0
- package/dist/server/shared/git-remote.d.ts +16 -0
- package/dist/server/shared/git-remote.d.ts.map +1 -0
- package/dist/server/shared/git-remote.js +72 -0
- package/dist/server/shared/git-remote.js.map +1 -0
- package/dist/server/shared/importable-providers.d.ts +7 -0
- package/dist/server/shared/importable-providers.d.ts.map +1 -0
- package/dist/server/shared/importable-providers.js +7 -0
- package/dist/server/shared/importable-providers.js.map +1 -0
- package/dist/server/shared/messages.d.ts +7148 -690
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +56 -4
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/utils/build-metadata-prompt.d.ts +14 -0
- package/dist/server/utils/build-metadata-prompt.d.ts.map +1 -0
- package/dist/server/utils/build-metadata-prompt.js +28 -0
- package/dist/server/utils/build-metadata-prompt.js.map +1 -0
- package/dist/server/utils/checkout-git.d.ts +3 -1
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +3 -0
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/dist/server/utils/github-remote.d.ts +3 -7
- package/dist/server/utils/github-remote.d.ts.map +1 -1
- package/dist/server/utils/github-remote.js +4 -70
- package/dist/server/utils/github-remote.js.map +1 -1
- package/dist/server/utils/paseo-config-schema.d.ts +625 -0
- package/dist/server/utils/paseo-config-schema.d.ts.map +1 -1
- package/dist/server/utils/paseo-config-schema.js +17 -0
- package/dist/server/utils/paseo-config-schema.js.map +1 -1
- package/dist/server/utils/wrap-user-instructions.d.ts +2 -0
- package/dist/server/utils/wrap-user-instructions.d.ts.map +1 -0
- package/dist/server/utils/wrap-user-instructions.js +13 -0
- package/dist/server/utils/wrap-user-instructions.js.map +1 -0
- package/dist/src/server/paseo-home.js +2 -2
- package/dist/src/server/paseo-home.js.map +1 -1
- package/dist/src/server/persisted-config.js +5 -4
- package/dist/src/server/persisted-config.js.map +1 -1
- package/dist/src/server/private-files.js +42 -0
- package/dist/src/server/private-files.js.map +1 -0
- package/dist/src/shared/messages.js +56 -4
- package/dist/src/shared/messages.js.map +1 -1
- package/dist/src/utils/paseo-config-schema.js +17 -0
- package/dist/src/utils/paseo-config-schema.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
|
-
import
|
|
3
|
-
import net from "node:net";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { createProviderEnvSpec, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
|
|
3
|
+
import path from "node:path";
|
|
6
4
|
import { findExecutable, isCommandAvailable } from "../../../utils/executable.js";
|
|
7
|
-
import {
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { createProviderEnvSpec } from "../provider-launch-config.js";
|
|
8
7
|
import { withTimeout } from "../../../utils/promise-timeout.js";
|
|
9
|
-
import { execCommand
|
|
8
|
+
import { execCommand } from "../../../utils/spawn.js";
|
|
10
9
|
import { buildToolCallDisplayModel } from "../../../shared/tool-call-display.js";
|
|
11
10
|
import { mapOpencodeToolCall } from "./opencode/tool-call-mapper.js";
|
|
11
|
+
import { OpenCodeServerManager } from "./opencode/server-manager.js";
|
|
12
12
|
import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, resolveBinaryVersion, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
|
|
13
13
|
import { runProviderTurn } from "./provider-runner.js";
|
|
14
14
|
import { renderPromptAttachmentAsText } from "../prompt-attachments.js";
|
|
15
|
+
import { createSdkOpenCodeClient, } from "./opencode/runtime.js";
|
|
15
16
|
const OPENCODE_CAPABILITIES = {
|
|
16
17
|
supportsStreaming: true,
|
|
17
18
|
supportsSessionPersistence: true,
|
|
@@ -22,6 +23,7 @@ const OPENCODE_CAPABILITIES = {
|
|
|
22
23
|
};
|
|
23
24
|
const OPENCODE_BUILD_MODE_ID = "build";
|
|
24
25
|
const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
|
|
26
|
+
const OPENCODE_STORAGE_SESSION_LIMIT = 200;
|
|
25
27
|
const DEFAULT_MODES = [
|
|
26
28
|
{
|
|
27
29
|
id: OPENCODE_BUILD_MODE_ID,
|
|
@@ -39,10 +41,46 @@ const DEFAULT_MODES = [
|
|
|
39
41
|
description: "Automatically approves all tool permission prompts for the session",
|
|
40
42
|
},
|
|
41
43
|
];
|
|
44
|
+
const OpenCodeStoredSessionSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
id: z.string().min(1),
|
|
47
|
+
directory: z.string().min(1),
|
|
48
|
+
title: z.string().nullable().optional(),
|
|
49
|
+
time: z
|
|
50
|
+
.object({
|
|
51
|
+
created: z.number().optional(),
|
|
52
|
+
updated: z.number().optional(),
|
|
53
|
+
})
|
|
54
|
+
.optional(),
|
|
55
|
+
})
|
|
56
|
+
.passthrough();
|
|
57
|
+
const OpenCodeStoredMessageSchema = z
|
|
58
|
+
.object({
|
|
59
|
+
id: z.string().min(1),
|
|
60
|
+
sessionID: z.string().min(1),
|
|
61
|
+
role: z.string().optional(),
|
|
62
|
+
time: z
|
|
63
|
+
.object({
|
|
64
|
+
created: z.number().optional(),
|
|
65
|
+
completed: z.number().optional(),
|
|
66
|
+
})
|
|
67
|
+
.optional(),
|
|
68
|
+
})
|
|
69
|
+
.passthrough();
|
|
70
|
+
const OpenCodeStoredPartSchema = z
|
|
71
|
+
.object({
|
|
72
|
+
type: z.string().optional(),
|
|
73
|
+
text: z.string().optional(),
|
|
74
|
+
time: z
|
|
75
|
+
.object({
|
|
76
|
+
start: z.number().optional(),
|
|
77
|
+
end: z.number().optional(),
|
|
78
|
+
})
|
|
79
|
+
.optional(),
|
|
80
|
+
})
|
|
81
|
+
.passthrough();
|
|
42
82
|
const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
|
|
43
83
|
const OPENCODE_PROVIDER_LIST_TIMEOUT_MS = 30000;
|
|
44
|
-
const OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
45
|
-
const OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS = 1000;
|
|
46
84
|
const OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS = [
|
|
47
85
|
{ name: "compact", description: "Compact the current session", argumentHint: "" },
|
|
48
86
|
{ name: "summarize", description: "Compact the current session", argumentHint: "" },
|
|
@@ -121,13 +159,6 @@ const OpencodeToolPartToTimelineItemSchema = OpencodeToolPartTimelineEnvelopeSch
|
|
|
121
159
|
output: part.output,
|
|
122
160
|
error: part.error,
|
|
123
161
|
}));
|
|
124
|
-
async function resolveOpenCodeBinary() {
|
|
125
|
-
const found = await findExecutable("opencode");
|
|
126
|
-
if (found) {
|
|
127
|
-
return found;
|
|
128
|
-
}
|
|
129
|
-
throw new Error("OpenCode binary not found. Install OpenCode (https://github.com/opencode-ai/opencode) and ensure it is available in your shell PATH.");
|
|
130
|
-
}
|
|
131
162
|
function toOpenCodeMcpConfig(config) {
|
|
132
163
|
if (config.type === "stdio") {
|
|
133
164
|
return {
|
|
@@ -237,22 +268,6 @@ function isAlreadyPresentMcpError(error) {
|
|
|
237
268
|
const normalized = toDiagnosticErrorMessage(error).toLowerCase();
|
|
238
269
|
return MCP_ALREADY_PRESENT_ERROR_TOKENS.some((token) => normalized.includes(token));
|
|
239
270
|
}
|
|
240
|
-
async function findAvailablePort() {
|
|
241
|
-
return new Promise((resolve, reject) => {
|
|
242
|
-
const server = net.createServer();
|
|
243
|
-
server.listen(0, () => {
|
|
244
|
-
const address = server.address();
|
|
245
|
-
if (address && typeof address === "object") {
|
|
246
|
-
const port = address.port;
|
|
247
|
-
server.close(() => resolve(port));
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
server.close(() => reject(new Error("Failed to get port")));
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
server.on("error", reject);
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
271
|
function resolvePartDedupeKey(part, partType) {
|
|
257
272
|
if (part.id.trim().length > 0) {
|
|
258
273
|
return `${partType}:${part.id}`;
|
|
@@ -485,6 +500,133 @@ function buildOpenCodePromptParts(prompt) {
|
|
|
485
500
|
}
|
|
486
501
|
return output;
|
|
487
502
|
}
|
|
503
|
+
function resolveOpenCodeStorageRoot() {
|
|
504
|
+
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
505
|
+
const dataHome = typeof xdgDataHome === "string" && xdgDataHome.trim().length > 0
|
|
506
|
+
? xdgDataHome
|
|
507
|
+
: path.join(homedir(), ".local", "share");
|
|
508
|
+
return path.join(dataHome, "opencode", "storage");
|
|
509
|
+
}
|
|
510
|
+
async function collectOpenCodePersistedAgentsFromStorage(storageRoot, options) {
|
|
511
|
+
const sessions = await readOpenCodeStoredSessions(path.join(storageRoot, "session"));
|
|
512
|
+
const limit = options?.limit ?? OPENCODE_STORAGE_SESSION_LIMIT;
|
|
513
|
+
const candidates = sessions
|
|
514
|
+
.filter((session) => !options?.cwd || session.directory === options.cwd)
|
|
515
|
+
.sort((left, right) => getOpenCodeSessionTimestamp(right) - getOpenCodeSessionTimestamp(left))
|
|
516
|
+
.slice(0, limit);
|
|
517
|
+
return await Promise.all(candidates.map((session) => buildOpenCodePersistedAgentDescriptor(storageRoot, session)));
|
|
518
|
+
}
|
|
519
|
+
async function readOpenCodeStoredSessions(sessionRoot) {
|
|
520
|
+
const files = await findJsonFiles(sessionRoot);
|
|
521
|
+
const sessions = [];
|
|
522
|
+
for (const file of files) {
|
|
523
|
+
const parsed = await readJsonFile(file, OpenCodeStoredSessionSchema);
|
|
524
|
+
if (parsed) {
|
|
525
|
+
sessions.push(parsed);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return sessions;
|
|
529
|
+
}
|
|
530
|
+
async function buildOpenCodePersistedAgentDescriptor(storageRoot, session) {
|
|
531
|
+
const timeline = await readOpenCodeSessionTimeline(storageRoot, session.id);
|
|
532
|
+
return {
|
|
533
|
+
provider: "opencode",
|
|
534
|
+
sessionId: session.id,
|
|
535
|
+
cwd: session.directory,
|
|
536
|
+
title: normalizeOpenCodeSessionTitle(session.title),
|
|
537
|
+
lastActivityAt: new Date(getOpenCodeSessionTimestamp(session)),
|
|
538
|
+
persistence: {
|
|
539
|
+
provider: "opencode",
|
|
540
|
+
sessionId: session.id,
|
|
541
|
+
nativeHandle: session.id,
|
|
542
|
+
metadata: {
|
|
543
|
+
provider: "opencode",
|
|
544
|
+
cwd: session.directory,
|
|
545
|
+
title: normalizeOpenCodeSessionTitle(session.title),
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
timeline,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
async function readOpenCodeSessionTimeline(storageRoot, sessionId) {
|
|
552
|
+
const messageRoot = path.join(storageRoot, "message", sessionId);
|
|
553
|
+
const messageFiles = await findJsonFiles(messageRoot);
|
|
554
|
+
const messages = [];
|
|
555
|
+
for (const file of messageFiles) {
|
|
556
|
+
const parsed = await readJsonFile(file, OpenCodeStoredMessageSchema);
|
|
557
|
+
if (parsed?.sessionID === sessionId) {
|
|
558
|
+
messages.push(parsed);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const timeline = [];
|
|
562
|
+
for (const message of messages.sort((left, right) => getOpenCodeMessageTimestamp(left) - getOpenCodeMessageTimestamp(right))) {
|
|
563
|
+
const text = await readOpenCodeMessageText(storageRoot, message.id);
|
|
564
|
+
if (!text) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (message.role === "user") {
|
|
568
|
+
timeline.push({ type: "user_message", text, messageId: message.id });
|
|
569
|
+
}
|
|
570
|
+
else if (message.role === "assistant") {
|
|
571
|
+
timeline.push({ type: "assistant_message", text });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return timeline;
|
|
575
|
+
}
|
|
576
|
+
async function readOpenCodeMessageText(storageRoot, messageId) {
|
|
577
|
+
const partRoot = path.join(storageRoot, "part", messageId);
|
|
578
|
+
const partFiles = await findJsonFiles(partRoot);
|
|
579
|
+
const parts = [];
|
|
580
|
+
for (const file of partFiles) {
|
|
581
|
+
const parsed = await readJsonFile(file, OpenCodeStoredPartSchema);
|
|
582
|
+
if (parsed?.type === "text" && typeof parsed.text === "string") {
|
|
583
|
+
parts.push(parsed);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return parts
|
|
587
|
+
.sort((left, right) => getOpenCodePartTimestamp(left) - getOpenCodePartTimestamp(right))
|
|
588
|
+
.map((part) => part.text?.trim() ?? "")
|
|
589
|
+
.filter(Boolean)
|
|
590
|
+
.join("\n\n");
|
|
591
|
+
}
|
|
592
|
+
async function findJsonFiles(root) {
|
|
593
|
+
let entries;
|
|
594
|
+
try {
|
|
595
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return [];
|
|
599
|
+
}
|
|
600
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
601
|
+
const entryPath = path.join(root, entry.name);
|
|
602
|
+
if (entry.isDirectory()) {
|
|
603
|
+
return findJsonFiles(entryPath);
|
|
604
|
+
}
|
|
605
|
+
return entry.isFile() && entry.name.endsWith(".json") ? [entryPath] : [];
|
|
606
|
+
}));
|
|
607
|
+
return files.flat();
|
|
608
|
+
}
|
|
609
|
+
async function readJsonFile(file, schema) {
|
|
610
|
+
try {
|
|
611
|
+
return schema.parse(JSON.parse(await readFile(file, "utf8")));
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function normalizeOpenCodeSessionTitle(title) {
|
|
618
|
+
const normalized = title?.trim();
|
|
619
|
+
return normalized ? normalized : null;
|
|
620
|
+
}
|
|
621
|
+
function getOpenCodeSessionTimestamp(session) {
|
|
622
|
+
return session.time?.updated ?? session.time?.created ?? 0;
|
|
623
|
+
}
|
|
624
|
+
function getOpenCodeMessageTimestamp(message) {
|
|
625
|
+
return message.time?.created ?? message.time?.completed ?? 0;
|
|
626
|
+
}
|
|
627
|
+
function getOpenCodePartTimestamp(part) {
|
|
628
|
+
return part.time?.start ?? part.time?.end ?? 0;
|
|
629
|
+
}
|
|
488
630
|
export const __openCodeInternals = {
|
|
489
631
|
buildOpenCodePromptParts,
|
|
490
632
|
buildOpenCodeModelContextWindowLookup,
|
|
@@ -502,243 +644,40 @@ export const __openCodeInternals = {
|
|
|
502
644
|
return OpenCodeAgentSession;
|
|
503
645
|
},
|
|
504
646
|
};
|
|
505
|
-
|
|
506
|
-
constructor(
|
|
507
|
-
this.
|
|
508
|
-
this.retiredServers = new Set();
|
|
509
|
-
this.startPromise = null;
|
|
510
|
-
this.forcedRefreshPromise = null;
|
|
511
|
-
this.logger = logger;
|
|
512
|
-
this.runtimeSettings = runtimeSettings;
|
|
513
|
-
this.runtimeSettingsKey = JSON.stringify(runtimeSettings ?? {});
|
|
647
|
+
class ProductionOpenCodeRuntime {
|
|
648
|
+
constructor(serverManager) {
|
|
649
|
+
this.serverManager = serverManager;
|
|
514
650
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (!OpenCodeServerManager.instance) {
|
|
518
|
-
OpenCodeServerManager.instance = new OpenCodeServerManager(logger, runtimeSettings);
|
|
519
|
-
OpenCodeServerManager.registerExitHandler();
|
|
520
|
-
}
|
|
521
|
-
else if (OpenCodeServerManager.instance.runtimeSettingsKey !== nextSettingsKey) {
|
|
522
|
-
logger.warn({
|
|
523
|
-
existingRuntimeSettings: OpenCodeServerManager.instance.runtimeSettingsKey,
|
|
524
|
-
requestedRuntimeSettings: nextSettingsKey,
|
|
525
|
-
}, "OpenCode server manager already initialized with different runtime settings");
|
|
526
|
-
}
|
|
527
|
-
return OpenCodeServerManager.instance;
|
|
651
|
+
async acquireServer(options) {
|
|
652
|
+
return this.serverManager.acquire(options);
|
|
528
653
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
OpenCodeServerManager.exitHandlerRegistered = true;
|
|
534
|
-
const cleanup = () => {
|
|
535
|
-
const instance = OpenCodeServerManager.instance;
|
|
536
|
-
void instance?.shutdown();
|
|
537
|
-
};
|
|
538
|
-
process.on("exit", cleanup);
|
|
539
|
-
process.on("SIGTERM", cleanup);
|
|
540
|
-
process.on("SIGINT", cleanup);
|
|
541
|
-
}
|
|
542
|
-
async ensureRunning() {
|
|
543
|
-
const acquisition = await this.acquire({ force: false });
|
|
544
|
-
acquisition.release();
|
|
545
|
-
return acquisition.server;
|
|
546
|
-
}
|
|
547
|
-
async acquire(options) {
|
|
548
|
-
const server = options.force
|
|
549
|
-
? await this.getForcedRefreshServer()
|
|
550
|
-
: await this.getCurrentServer();
|
|
551
|
-
server.refCount += 1;
|
|
552
|
-
let released = false;
|
|
553
|
-
return {
|
|
554
|
-
server: { port: server.port, url: server.url },
|
|
555
|
-
release: () => {
|
|
556
|
-
if (released) {
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
released = true;
|
|
560
|
-
server.refCount -= 1;
|
|
561
|
-
this.cleanupRetiredServers();
|
|
562
|
-
},
|
|
563
|
-
};
|
|
654
|
+
async ensureServerRunning() {
|
|
655
|
+
return this.serverManager.ensureRunning();
|
|
564
656
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
return this.forcedRefreshPromise;
|
|
568
|
-
}
|
|
569
|
-
this.forcedRefreshPromise = Promise.resolve()
|
|
570
|
-
.then(async () => {
|
|
571
|
-
await this.rotateCurrentServer();
|
|
572
|
-
return this.getCurrentServer();
|
|
573
|
-
})
|
|
574
|
-
.finally(() => {
|
|
575
|
-
this.forcedRefreshPromise = null;
|
|
576
|
-
});
|
|
577
|
-
return this.forcedRefreshPromise;
|
|
578
|
-
}
|
|
579
|
-
async getCurrentServer() {
|
|
580
|
-
if (this.startPromise) {
|
|
581
|
-
return this.startPromise;
|
|
582
|
-
}
|
|
583
|
-
if (this.currentServer && !this.currentServer.process.killed) {
|
|
584
|
-
return this.currentServer;
|
|
585
|
-
}
|
|
586
|
-
this.startPromise = this.startServer();
|
|
587
|
-
try {
|
|
588
|
-
const result = await this.startPromise;
|
|
589
|
-
if (!result.retired) {
|
|
590
|
-
this.currentServer = result;
|
|
591
|
-
}
|
|
592
|
-
return result;
|
|
593
|
-
}
|
|
594
|
-
finally {
|
|
595
|
-
this.startPromise = null;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
async rotateCurrentServer() {
|
|
599
|
-
const existing = this.currentServer;
|
|
600
|
-
if (existing) {
|
|
601
|
-
existing.retired = true;
|
|
602
|
-
this.retiredServers.add(existing);
|
|
603
|
-
this.currentServer = null;
|
|
604
|
-
this.cleanupRetiredServers();
|
|
605
|
-
}
|
|
606
|
-
if (this.startPromise) {
|
|
607
|
-
const pending = await this.startPromise;
|
|
608
|
-
pending.retired = true;
|
|
609
|
-
this.retiredServers.add(pending);
|
|
610
|
-
this.currentServer = null;
|
|
611
|
-
this.cleanupRetiredServers();
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
async startServer() {
|
|
615
|
-
const port = await findAvailablePort();
|
|
616
|
-
const url = `http://127.0.0.1:${port}`;
|
|
617
|
-
const launchPrefix = await resolveProviderCommandPrefix(this.runtimeSettings?.command, resolveOpenCodeBinary);
|
|
618
|
-
return new Promise((resolve, reject) => {
|
|
619
|
-
const serverProcess = spawnProcess(launchPrefix.command, [...launchPrefix.args, "serve", "--port", String(port)], {
|
|
620
|
-
detached: process.platform !== "win32",
|
|
621
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
622
|
-
...createProviderEnvSpec({ runtimeSettings: this.runtimeSettings }),
|
|
623
|
-
});
|
|
624
|
-
let started = false;
|
|
625
|
-
let stderrBuffer = "";
|
|
626
|
-
let stdoutBuffer = "";
|
|
627
|
-
const STARTUP_BUFFER_CAP = 8192;
|
|
628
|
-
const appendCapped = (current, chunk) => {
|
|
629
|
-
if (current.length >= STARTUP_BUFFER_CAP) {
|
|
630
|
-
return current;
|
|
631
|
-
}
|
|
632
|
-
const remaining = STARTUP_BUFFER_CAP - current.length;
|
|
633
|
-
return current + chunk.slice(0, remaining);
|
|
634
|
-
};
|
|
635
|
-
const buildStartupErrorMessage = (headline) => {
|
|
636
|
-
const sections = [headline];
|
|
637
|
-
const stderrTrimmed = stderrBuffer.trim();
|
|
638
|
-
if (stderrTrimmed.length > 0) {
|
|
639
|
-
sections.push(`stderr: ${stderrTrimmed}`);
|
|
640
|
-
}
|
|
641
|
-
const stdoutTrimmed = stdoutBuffer.trim();
|
|
642
|
-
if (stdoutTrimmed.length > 0) {
|
|
643
|
-
sections.push(`stdout: ${stdoutTrimmed}`);
|
|
644
|
-
}
|
|
645
|
-
return sections.join("\n");
|
|
646
|
-
};
|
|
647
|
-
const timeout = setTimeout(() => {
|
|
648
|
-
if (!started) {
|
|
649
|
-
reject(new Error(buildStartupErrorMessage("OpenCode server startup timeout")));
|
|
650
|
-
}
|
|
651
|
-
}, 30000);
|
|
652
|
-
serverProcess.stdout?.on("data", (data) => {
|
|
653
|
-
const output = data.toString();
|
|
654
|
-
stdoutBuffer = appendCapped(stdoutBuffer, output);
|
|
655
|
-
if (output.includes("listening on") && !started) {
|
|
656
|
-
started = true;
|
|
657
|
-
clearTimeout(timeout);
|
|
658
|
-
resolve({
|
|
659
|
-
process: serverProcess,
|
|
660
|
-
port,
|
|
661
|
-
url,
|
|
662
|
-
refCount: 0,
|
|
663
|
-
retired: false,
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
serverProcess.stderr?.on("data", (data) => {
|
|
668
|
-
const output = data.toString();
|
|
669
|
-
stderrBuffer = appendCapped(stderrBuffer, output);
|
|
670
|
-
this.logger.error({ stderr: output.trim() }, "OpenCode server stderr");
|
|
671
|
-
});
|
|
672
|
-
serverProcess.on("error", (error) => {
|
|
673
|
-
clearTimeout(timeout);
|
|
674
|
-
const headline = error instanceof Error ? error.message : String(error);
|
|
675
|
-
reject(new Error(buildStartupErrorMessage(headline)));
|
|
676
|
-
});
|
|
677
|
-
serverProcess.on("exit", (code) => {
|
|
678
|
-
if (!started) {
|
|
679
|
-
clearTimeout(timeout);
|
|
680
|
-
reject(new Error(buildStartupErrorMessage(`OpenCode server exited with code ${code}`)));
|
|
681
|
-
}
|
|
682
|
-
if (this.currentServer?.process === serverProcess) {
|
|
683
|
-
this.currentServer = null;
|
|
684
|
-
}
|
|
685
|
-
for (const retired of Array.from(this.retiredServers)) {
|
|
686
|
-
if (retired.process === serverProcess) {
|
|
687
|
-
this.retiredServers.delete(retired);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
});
|
|
657
|
+
createClient(options) {
|
|
658
|
+
return createSdkOpenCodeClient(options);
|
|
692
659
|
}
|
|
693
660
|
async shutdown() {
|
|
694
|
-
|
|
695
|
-
...(this.currentServer ? [this.currentServer] : []),
|
|
696
|
-
...Array.from(this.retiredServers),
|
|
697
|
-
];
|
|
698
|
-
await Promise.all(servers.map((server) => this.killServer(server)));
|
|
699
|
-
this.currentServer = null;
|
|
700
|
-
this.retiredServers.clear();
|
|
701
|
-
}
|
|
702
|
-
cleanupRetiredServers() {
|
|
703
|
-
for (const server of Array.from(this.retiredServers)) {
|
|
704
|
-
if (server.refCount === 0) {
|
|
705
|
-
this.retiredServers.delete(server);
|
|
706
|
-
void this.killServer(server);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
async killServer(server) {
|
|
711
|
-
if (server.process.killed) {
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
const result = await terminateWithTreeKill(server.process, {
|
|
715
|
-
gracefulTimeoutMs: OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS,
|
|
716
|
-
forceTimeoutMs: OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS,
|
|
717
|
-
onForceSignal: () => {
|
|
718
|
-
this.logger.warn({ timeoutMs: OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS }, "OpenCode server did not exit after SIGTERM; sending SIGKILL");
|
|
719
|
-
},
|
|
720
|
-
});
|
|
721
|
-
if (result === "kill-timeout") {
|
|
722
|
-
this.logger.warn({ timeoutMs: OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS }, "OpenCode server did not report exit after SIGKILL");
|
|
723
|
-
}
|
|
661
|
+
await this.serverManager.shutdown();
|
|
724
662
|
}
|
|
725
663
|
}
|
|
726
|
-
OpenCodeServerManager.instance = null;
|
|
727
|
-
OpenCodeServerManager.exitHandlerRegistered = false;
|
|
728
664
|
export class OpenCodeAgentClient {
|
|
729
|
-
constructor(logger, runtimeSettings) {
|
|
665
|
+
constructor(logger, runtimeSettings, storageRoot, deps = {}) {
|
|
730
666
|
this.provider = "opencode";
|
|
731
667
|
this.capabilities = OPENCODE_CAPABILITIES;
|
|
732
668
|
this.modelContextWindows = new Map();
|
|
733
669
|
this.logger = logger.child({ module: "agent", provider: "opencode" });
|
|
734
670
|
this.runtimeSettings = runtimeSettings;
|
|
735
|
-
this.
|
|
671
|
+
this.storageRoot = storageRoot ?? resolveOpenCodeStorageRoot();
|
|
672
|
+
this.runtime =
|
|
673
|
+
deps.runtime ??
|
|
674
|
+
new ProductionOpenCodeRuntime(OpenCodeServerManager.getInstance(this.logger, runtimeSettings));
|
|
736
675
|
}
|
|
737
676
|
async createSession(config, _launchContext, options) {
|
|
738
677
|
const openCodeConfig = this.assertConfig(config);
|
|
739
|
-
const acquisition = await this.
|
|
678
|
+
const acquisition = await this.runtime.acquireServer({ force: false });
|
|
740
679
|
const { url } = acquisition.server;
|
|
741
|
-
const client =
|
|
680
|
+
const client = this.runtime.createClient({
|
|
742
681
|
baseUrl: url,
|
|
743
682
|
directory: openCodeConfig.cwd,
|
|
744
683
|
});
|
|
@@ -770,9 +709,9 @@ export class OpenCodeAgentClient {
|
|
|
770
709
|
...overrides,
|
|
771
710
|
};
|
|
772
711
|
const openCodeConfig = this.assertConfig(config);
|
|
773
|
-
const acquisition = await this.
|
|
712
|
+
const acquisition = await this.runtime.acquireServer({ force: false });
|
|
774
713
|
const { url } = acquisition.server;
|
|
775
|
-
const client =
|
|
714
|
+
const client = this.runtime.createClient({
|
|
776
715
|
baseUrl: url,
|
|
777
716
|
directory: openCodeConfig.cwd,
|
|
778
717
|
});
|
|
@@ -786,9 +725,9 @@ export class OpenCodeAgentClient {
|
|
|
786
725
|
}
|
|
787
726
|
}
|
|
788
727
|
async listModels(options) {
|
|
789
|
-
const acquisition = await this.
|
|
728
|
+
const acquisition = await this.runtime.acquireServer({ force: options.force });
|
|
790
729
|
const { url } = acquisition.server;
|
|
791
|
-
const client =
|
|
730
|
+
const client = this.runtime.createClient({
|
|
792
731
|
baseUrl: url,
|
|
793
732
|
directory: options.cwd,
|
|
794
733
|
});
|
|
@@ -832,10 +771,10 @@ export class OpenCodeAgentClient {
|
|
|
832
771
|
}
|
|
833
772
|
}
|
|
834
773
|
async listModes(options) {
|
|
835
|
-
const acquisition = await this.
|
|
774
|
+
const acquisition = await this.runtime.acquireServer({ force: options.force });
|
|
836
775
|
const { url } = acquisition.server;
|
|
837
776
|
const directory = options.cwd;
|
|
838
|
-
const client =
|
|
777
|
+
const client = this.runtime.createClient({ baseUrl: url, directory });
|
|
839
778
|
try {
|
|
840
779
|
const response = await withTimeout(client.app.agents({ directory }), 10000, "OpenCode app.agents timed out after 10s");
|
|
841
780
|
if (response.error || !response.data) {
|
|
@@ -854,9 +793,8 @@ export class OpenCodeAgentClient {
|
|
|
854
793
|
acquisition.release();
|
|
855
794
|
}
|
|
856
795
|
}
|
|
857
|
-
async listPersistedAgents(
|
|
858
|
-
|
|
859
|
-
return [];
|
|
796
|
+
async listPersistedAgents(options) {
|
|
797
|
+
return collectOpenCodePersistedAgentsFromStorage(this.storageRoot, options);
|
|
860
798
|
}
|
|
861
799
|
async isAvailable() {
|
|
862
800
|
const command = this.runtimeSettings?.command;
|
|
@@ -873,7 +811,7 @@ export class OpenCodeAgentClient {
|
|
|
873
811
|
let modelsValue = "Not checked";
|
|
874
812
|
let status = formatDiagnosticStatus(available);
|
|
875
813
|
try {
|
|
876
|
-
const { url } = await this.
|
|
814
|
+
const { url } = await this.runtime.ensureServerRunning();
|
|
877
815
|
serverStatus = `Running (${url})`;
|
|
878
816
|
}
|
|
879
817
|
catch (error) {
|