oh-my-opencode 4.18.1 → 4.19.0
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/.agents/skills/codex-qa/scripts/lsp-e2e.sh +39 -16
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
- package/README.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +4 -4
- package/README.ru.md +2 -2
- package/README.zh-cn.md +2 -2
- package/bin/AGENTS.md +33 -0
- package/dist/agents/atlas/agent.d.ts +6 -5
- package/dist/agents/sisyphus/index.d.ts +5 -0
- package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
- package/dist/agents/sisyphus-junior/index.d.ts +2 -0
- package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +690 -139
- package/dist/cli-node/index.js +690 -139
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/browser-automation.d.ts +1 -0
- package/dist/config/schema/commands.d.ts +1 -3
- package/dist/config/schema/default-mode.d.ts +1 -1
- package/dist/config/schema/goal.d.ts +7 -0
- package/dist/config/schema/hooks.d.ts +1 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
- package/dist/config/schema/ralph-loop.d.ts +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/create-hooks.d.ts +1 -1
- package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
- package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
- package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
- package/dist/hooks/comment-checker/hook.d.ts +8 -1
- package/dist/hooks/goal/command-arguments.d.ts +13 -0
- package/dist/hooks/goal/controller.d.ts +26 -0
- package/dist/hooks/goal/index.d.ts +23 -0
- package/dist/hooks/goal/prompt.d.ts +3 -0
- package/dist/hooks/goal/store.d.ts +8 -0
- package/dist/hooks/goal/tools.d.ts +10 -0
- package/dist/hooks/goal/types.d.ts +93 -0
- package/dist/hooks/goal/validation.d.ts +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/keyword-detector/hook.d.ts +1 -2
- package/dist/hooks/ralph-loop/types.d.ts +1 -1
- package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
- package/dist/index.js +3706 -4329
- package/dist/oh-my-opencode.schema.json +23 -19
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
- package/dist/plugin/chat-message/types.d.ts +23 -10
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
- package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
- package/dist/plugin/messages-transform.d.ts +1 -0
- package/dist/plugin/stop-continuation.d.ts +2 -2
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
- package/dist/shared/system-directive.d.ts +0 -1
- package/dist/skills/ulw-plan/SKILL.md +11 -7
- package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/dist/skills/ulw-research/SKILL.md +3 -3
- package/dist/tools/delegate-task/types.d.ts +0 -1
- package/dist/tui.js +115 -28
- package/package.json +14 -14
- package/packages/git-bash-mcp/dist/cli.js +81 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
- package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
- package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
- package/packages/lsp-daemon/dist/cli.js +262 -63
- package/packages/lsp-daemon/dist/client.js +194 -50
- package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
- package/packages/lsp-daemon/dist/daemon-client.js +26 -2
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
- package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
- package/packages/lsp-daemon/dist/index.js +262 -63
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +79 -23
- package/packages/lsp-tools-mcp/dist/cli.js +82 -20
- package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
- package/packages/lsp-tools-mcp/dist/tools.js +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +195 -56
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +148 -61
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +4 -72
- package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +213 -24
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +25 -4
- package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +13 -20
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +38 -22
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
- package/dist/skills/ultraresearch/SKILL.md +0 -10
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
|
@@ -124,7 +124,7 @@ export async function aggregateDiagnosticsForDirectory(
|
|
|
124
124
|
const maxConcurrency = Math.max(1, options.maxConcurrency ?? DIRECTORY_DIAGNOSTICS_MAX_CONCURRENCY);
|
|
125
125
|
|
|
126
126
|
options.signal?.throwIfAborted();
|
|
127
|
-
const client = await manager.getClient(root, server);
|
|
127
|
+
const client = await manager.getClient(root, server, options.signal);
|
|
128
128
|
try {
|
|
129
129
|
let nextIndex = 0;
|
|
130
130
|
const workers = Array.from({ length: Math.min(maxConcurrency, filesToProcess.length) }, async () => {
|
|
@@ -21,6 +21,9 @@ function errorResponse(id, code, message, data) {
|
|
|
21
21
|
function jsonRpcId(value) {
|
|
22
22
|
return typeof value === "string" || typeof value === "number" || value === null ? value : null;
|
|
23
23
|
}
|
|
24
|
+
function messageFromError(error) {
|
|
25
|
+
return error instanceof Error ? error.message : String(error);
|
|
26
|
+
}
|
|
24
27
|
// ../mcp-stdio-core/src/transport.ts
|
|
25
28
|
var HEADER_SEPARATOR = Buffer.from(`\r
|
|
26
29
|
\r
|
|
@@ -43,16 +46,45 @@ async function* readStdioJsonRpcMessages(input) {
|
|
|
43
46
|
yield parseJsonPayload(trailing, "line");
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
|
-
function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
49
|
+
async function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
47
50
|
const body = JSON.stringify(response);
|
|
48
|
-
|
|
49
|
-
output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
51
|
+
const payload = responseMode === "framed" ? `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
50
52
|
\r
|
|
51
|
-
${body}`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
${body}` : `${body}
|
|
54
|
+
`;
|
|
55
|
+
await writeChunk(output, payload);
|
|
56
|
+
}
|
|
57
|
+
function writeChunk(output, chunk) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
let settled = false;
|
|
60
|
+
const onError = (error) => {
|
|
61
|
+
if (settled)
|
|
62
|
+
return;
|
|
63
|
+
settled = true;
|
|
64
|
+
reject(error);
|
|
65
|
+
};
|
|
66
|
+
output.once("error", onError);
|
|
67
|
+
try {
|
|
68
|
+
output.write(chunk, (error) => {
|
|
69
|
+
if (settled)
|
|
70
|
+
return;
|
|
71
|
+
settled = true;
|
|
72
|
+
if (error) {
|
|
73
|
+
queueMicrotask(() => output.removeListener("error", onError));
|
|
74
|
+
reject(error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
output.removeListener("error", onError);
|
|
78
|
+
resolve();
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
output.removeListener("error", onError);
|
|
82
|
+
if (settled)
|
|
83
|
+
return;
|
|
84
|
+
settled = true;
|
|
85
|
+
reject(error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
56
88
|
}
|
|
57
89
|
function readNextMessage(buffer) {
|
|
58
90
|
if (buffer.length === 0)
|
|
@@ -148,39 +180,69 @@ async function runJsonRpcStdioServer(config) {
|
|
|
148
180
|
break;
|
|
149
181
|
idleTimer.arm();
|
|
150
182
|
if (message.kind === "parse_error") {
|
|
151
|
-
handleParseError(message, config, log)
|
|
183
|
+
if (!await handleParseError(message, config, log))
|
|
184
|
+
break;
|
|
152
185
|
continue;
|
|
153
186
|
}
|
|
154
|
-
await handleRequest(message, config, log)
|
|
187
|
+
if (!await handleRequest(message, config, log))
|
|
188
|
+
break;
|
|
155
189
|
}
|
|
156
190
|
} finally {
|
|
157
191
|
idleTimer.clear();
|
|
158
192
|
log("stdio_stopped");
|
|
159
193
|
}
|
|
160
194
|
}
|
|
161
|
-
function handleParseError(message, config, log) {
|
|
195
|
+
async function handleParseError(message, config, log) {
|
|
162
196
|
log("parse_error", { message: message.message });
|
|
163
197
|
const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
|
|
164
|
-
if (response
|
|
165
|
-
|
|
166
|
-
|
|
198
|
+
if (response === undefined)
|
|
199
|
+
return true;
|
|
200
|
+
return writeResponse(response, {
|
|
201
|
+
output: config.output,
|
|
202
|
+
responseMode: message.responseMode,
|
|
203
|
+
log
|
|
204
|
+
});
|
|
167
205
|
}
|
|
168
206
|
async function handleRequest(message, config, log) {
|
|
169
207
|
const parsed = message.payload;
|
|
170
208
|
const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
|
|
171
209
|
const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
|
|
172
210
|
log("request", { id: id === null ? null : String(id), method });
|
|
211
|
+
let response;
|
|
173
212
|
try {
|
|
174
|
-
|
|
175
|
-
if (response === undefined)
|
|
176
|
-
return;
|
|
177
|
-
writeStdioJsonRpcResponse(config.output, response, message.responseMode);
|
|
178
|
-
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
213
|
+
response = await config.handler(parsed, config.handlerOptions);
|
|
179
214
|
} catch (error) {
|
|
180
215
|
if (config.onHandlerError === undefined)
|
|
181
216
|
throw error;
|
|
182
217
|
config.onHandlerError(error);
|
|
218
|
+
return true;
|
|
183
219
|
}
|
|
220
|
+
if (response === undefined)
|
|
221
|
+
return true;
|
|
222
|
+
if (!await writeResponse(response, {
|
|
223
|
+
output: config.output,
|
|
224
|
+
responseMode: message.responseMode,
|
|
225
|
+
log
|
|
226
|
+
}))
|
|
227
|
+
return false;
|
|
228
|
+
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
async function writeResponse(response, context) {
|
|
232
|
+
try {
|
|
233
|
+
await writeStdioJsonRpcResponse(context.output, response, context.responseMode);
|
|
234
|
+
return true;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
if (!isTerminalOutputError(error))
|
|
237
|
+
throw error;
|
|
238
|
+
context.log("output_error", { message: messageFromError(error) });
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function isTerminalOutputError(error) {
|
|
243
|
+
if (!(error instanceof Error) || !("code" in error))
|
|
244
|
+
return false;
|
|
245
|
+
return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
|
|
184
246
|
}
|
|
185
247
|
function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
|
|
186
248
|
let timer = null;
|
|
@@ -4241,7 +4303,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4241
4303
|
const fileErrors = [];
|
|
4242
4304
|
const maxConcurrency = Math.max(1, options.maxConcurrency ?? DIRECTORY_DIAGNOSTICS_MAX_CONCURRENCY);
|
|
4243
4305
|
options.signal?.throwIfAborted();
|
|
4244
|
-
const client = await manager.getClient(root, server2);
|
|
4306
|
+
const client = await manager.getClient(root, server2, options.signal);
|
|
4245
4307
|
try {
|
|
4246
4308
|
let nextIndex = 0;
|
|
4247
4309
|
const workers = Array.from({ length: Math.min(maxConcurrency, filesToProcess.length) }, async () => {
|
|
@@ -4999,14 +5061,14 @@ function requestedProtocolVersion(params) {
|
|
|
4999
5061
|
}
|
|
5000
5062
|
|
|
5001
5063
|
// src/daemon-client.ts
|
|
5002
|
-
import { connect
|
|
5064
|
+
import { connect } from "node:net";
|
|
5003
5065
|
import { homedir as homedir3 } from "node:os";
|
|
5004
5066
|
import { join as join7 } from "node:path";
|
|
5005
5067
|
|
|
5006
5068
|
// src/ensure-daemon.ts
|
|
5007
5069
|
import { spawn as spawn2 } from "node:child_process";
|
|
5008
5070
|
import { closeSync as closeSync2, mkdirSync as mkdirSync3, openSync as openSync2 } from "node:fs";
|
|
5009
|
-
import {
|
|
5071
|
+
import { Socket } from "node:net";
|
|
5010
5072
|
import { dirname as dirname8 } from "node:path";
|
|
5011
5073
|
import { execPath } from "node:process";
|
|
5012
5074
|
|
|
@@ -5413,42 +5475,48 @@ class DaemonUnreachableError extends Error {
|
|
|
5413
5475
|
async function ensureDaemonRunning(paths, deps = defaultEnsureDaemonDeps(), options = {}) {
|
|
5414
5476
|
const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
|
|
5415
5477
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
5416
|
-
|
|
5478
|
+
const signal = options.signal;
|
|
5479
|
+
throwIfAborted(signal);
|
|
5480
|
+
if (await awaitWithSignal2(deps.probe(paths, signal), signal))
|
|
5417
5481
|
return;
|
|
5482
|
+
throwIfAborted(signal);
|
|
5418
5483
|
deps.spawnDaemon(paths);
|
|
5419
|
-
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs);
|
|
5484
|
+
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal);
|
|
5420
5485
|
}
|
|
5421
|
-
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs) {
|
|
5486
|
+
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal) {
|
|
5422
5487
|
const deadline = deps.now() + readyTimeoutMs;
|
|
5423
5488
|
for (;; ) {
|
|
5424
|
-
|
|
5489
|
+
throwIfAborted(signal);
|
|
5490
|
+
if (await awaitWithSignal2(deps.probe(paths, signal), signal))
|
|
5425
5491
|
return;
|
|
5426
5492
|
if (deps.now() >= deadline)
|
|
5427
5493
|
throw new DaemonUnreachableError(paths.socket);
|
|
5428
|
-
await deps.sleep(pollIntervalMs);
|
|
5494
|
+
await awaitWithSignal2(deps.sleep(pollIntervalMs, signal), signal);
|
|
5429
5495
|
}
|
|
5430
5496
|
}
|
|
5431
|
-
async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
5497
|
+
async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
5432
5498
|
const token = readAuthToken(paths);
|
|
5433
5499
|
if (!token)
|
|
5434
5500
|
return false;
|
|
5435
|
-
return await pingDaemon(paths, token, timeoutMs) !== null;
|
|
5501
|
+
return await pingDaemon(paths, token, timeoutMs, signal) !== null;
|
|
5436
5502
|
}
|
|
5437
|
-
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
5503
|
+
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
5438
5504
|
return new Promise((resolve9) => {
|
|
5439
|
-
const socket =
|
|
5505
|
+
const socket = new Socket;
|
|
5440
5506
|
let settled = false;
|
|
5507
|
+
let timer;
|
|
5441
5508
|
const finish = (value) => {
|
|
5442
5509
|
if (settled)
|
|
5443
5510
|
return;
|
|
5444
5511
|
settled = true;
|
|
5512
|
+
if (timer !== undefined)
|
|
5513
|
+
clearTimeout(timer);
|
|
5514
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5445
5515
|
socket.destroy();
|
|
5446
5516
|
resolve9(value);
|
|
5447
5517
|
};
|
|
5448
|
-
const
|
|
5449
|
-
timer.unref?.();
|
|
5518
|
+
const onAbort = () => finish(null);
|
|
5450
5519
|
const decoder = createLineDecoder((message) => {
|
|
5451
|
-
clearTimeout(timer);
|
|
5452
5520
|
finish(parsePingResponse(message));
|
|
5453
5521
|
});
|
|
5454
5522
|
socket.once("connect", () => {
|
|
@@ -5456,9 +5524,16 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
|
5456
5524
|
});
|
|
5457
5525
|
socket.on("data", (chunk) => decoder.push(chunk));
|
|
5458
5526
|
socket.once("error", () => {
|
|
5459
|
-
clearTimeout(timer);
|
|
5460
5527
|
finish(null);
|
|
5461
5528
|
});
|
|
5529
|
+
timer = setTimeout(() => finish(null), timeoutMs);
|
|
5530
|
+
timer.unref?.();
|
|
5531
|
+
if (signal?.aborted) {
|
|
5532
|
+
onAbort();
|
|
5533
|
+
return;
|
|
5534
|
+
}
|
|
5535
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5536
|
+
socket.connect(paths.socket);
|
|
5462
5537
|
});
|
|
5463
5538
|
}
|
|
5464
5539
|
function spawnDaemonProcess(paths) {
|
|
@@ -5476,14 +5551,62 @@ function spawnDaemonProcess(paths) {
|
|
|
5476
5551
|
}
|
|
5477
5552
|
function defaultEnsureDaemonDeps() {
|
|
5478
5553
|
return {
|
|
5479
|
-
probe: (paths) => probeDaemon(paths),
|
|
5554
|
+
probe: (paths, signal) => probeDaemon(paths, PROBE_TIMEOUT_MS, signal),
|
|
5480
5555
|
spawnDaemon: (paths) => spawnDaemonProcess(paths),
|
|
5481
|
-
sleep: (ms) =>
|
|
5482
|
-
setTimeout(resolve9, ms);
|
|
5483
|
-
}),
|
|
5556
|
+
sleep: (ms, signal) => sleepWithSignal(ms, signal),
|
|
5484
5557
|
now: () => Date.now()
|
|
5485
5558
|
};
|
|
5486
5559
|
}
|
|
5560
|
+
function sleepWithSignal(ms, signal) {
|
|
5561
|
+
return new Promise((resolve9) => {
|
|
5562
|
+
let settled = false;
|
|
5563
|
+
const finish = () => {
|
|
5564
|
+
if (settled)
|
|
5565
|
+
return;
|
|
5566
|
+
settled = true;
|
|
5567
|
+
clearTimeout(timer);
|
|
5568
|
+
signal?.removeEventListener("abort", finish);
|
|
5569
|
+
resolve9();
|
|
5570
|
+
};
|
|
5571
|
+
const timer = setTimeout(finish, ms);
|
|
5572
|
+
if (signal?.aborted) {
|
|
5573
|
+
finish();
|
|
5574
|
+
return;
|
|
5575
|
+
}
|
|
5576
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
5577
|
+
});
|
|
5578
|
+
}
|
|
5579
|
+
function awaitWithSignal2(promise, signal) {
|
|
5580
|
+
if (!signal)
|
|
5581
|
+
return promise;
|
|
5582
|
+
if (signal.aborted)
|
|
5583
|
+
return Promise.reject(abortError3(signal));
|
|
5584
|
+
return new Promise((resolve9, reject) => {
|
|
5585
|
+
let settled = false;
|
|
5586
|
+
const finish = (run) => {
|
|
5587
|
+
if (settled)
|
|
5588
|
+
return;
|
|
5589
|
+
settled = true;
|
|
5590
|
+
signal.removeEventListener("abort", onAbort);
|
|
5591
|
+
run();
|
|
5592
|
+
};
|
|
5593
|
+
const onAbort = () => finish(() => reject(abortError3(signal)));
|
|
5594
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5595
|
+
promise.then((value) => finish(() => resolve9(value)), (error) => finish(() => reject(error)));
|
|
5596
|
+
});
|
|
5597
|
+
}
|
|
5598
|
+
function throwIfAborted(signal) {
|
|
5599
|
+
if (signal?.aborted)
|
|
5600
|
+
throw abortError3(signal);
|
|
5601
|
+
}
|
|
5602
|
+
function abortError3(signal) {
|
|
5603
|
+
const reason = signal.reason;
|
|
5604
|
+
if (reason instanceof Error)
|
|
5605
|
+
return reason;
|
|
5606
|
+
const error = new Error(typeof reason === "string" ? reason : "daemon startup cancelled");
|
|
5607
|
+
error.name = "AbortError";
|
|
5608
|
+
return error;
|
|
5609
|
+
}
|
|
5487
5610
|
function parsePingResponse(message) {
|
|
5488
5611
|
if (!message || typeof message !== "object" || Array.isArray(message))
|
|
5489
5612
|
return null;
|
|
@@ -5627,14 +5750,14 @@ class DaemonRequestCancelledError extends DaemonRequestError {
|
|
|
5627
5750
|
async function callToolViaDaemon(name, args, options) {
|
|
5628
5751
|
const context = requireContext(options.context);
|
|
5629
5752
|
const paths = options.paths ?? daemonPaths();
|
|
5630
|
-
const ensure = options.ensure ?? ensureDaemonRunning;
|
|
5753
|
+
const ensure = options.ensure ?? ((ensurePaths, signal) => ensureDaemonRunning(ensurePaths, undefined, signal === undefined ? {} : { signal }));
|
|
5631
5754
|
const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
5632
5755
|
const requestArgs = withContext(args, context);
|
|
5633
5756
|
let lastError;
|
|
5634
5757
|
let authRefreshUsed = false;
|
|
5635
5758
|
for (let attempt = 0;attempt < 3; attempt += 1) {
|
|
5636
5759
|
try {
|
|
5637
|
-
await
|
|
5760
|
+
await ensureDaemonAvailable(paths, ensure, options.signal);
|
|
5638
5761
|
const token = readAuthToken(paths);
|
|
5639
5762
|
if (!token)
|
|
5640
5763
|
throw new DaemonRequestError("daemon auth token missing", false);
|
|
@@ -5676,6 +5799,27 @@ function requireContext(context) {
|
|
|
5676
5799
|
function withContext(args, context) {
|
|
5677
5800
|
return { ...args, [CONTEXT_KEY]: context };
|
|
5678
5801
|
}
|
|
5802
|
+
function ensureDaemonAvailable(paths, ensure, signal) {
|
|
5803
|
+
if (!signal)
|
|
5804
|
+
return ensure(paths);
|
|
5805
|
+
return new Promise((resolve9, reject) => {
|
|
5806
|
+
let settled = false;
|
|
5807
|
+
const finish = (run) => {
|
|
5808
|
+
if (settled)
|
|
5809
|
+
return;
|
|
5810
|
+
settled = true;
|
|
5811
|
+
signal.removeEventListener("abort", onAbort);
|
|
5812
|
+
run();
|
|
5813
|
+
};
|
|
5814
|
+
const onAbort = () => finish(() => reject(new DaemonRequestCancelledError(false)));
|
|
5815
|
+
if (signal.aborted) {
|
|
5816
|
+
onAbort();
|
|
5817
|
+
return;
|
|
5818
|
+
}
|
|
5819
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5820
|
+
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve9()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5679
5823
|
function daemonUnreachableResult(paths, error) {
|
|
5680
5824
|
const text2 = [
|
|
5681
5825
|
`LSP daemon unreachable: ${errorText(error)}.`,
|
|
@@ -5689,7 +5833,7 @@ function daemonUnreachableResult(paths, error) {
|
|
|
5689
5833
|
}
|
|
5690
5834
|
function sendToolCall(paths, token, name, args, options) {
|
|
5691
5835
|
return new Promise((resolve9, reject) => {
|
|
5692
|
-
const socket =
|
|
5836
|
+
const socket = connect(paths.socket);
|
|
5693
5837
|
const requestId = allocateProxyRequestId();
|
|
5694
5838
|
let settled = false;
|
|
5695
5839
|
let requestWritten = false;
|
|
@@ -5790,38 +5934,93 @@ function errorText(error) {
|
|
|
5790
5934
|
}
|
|
5791
5935
|
|
|
5792
5936
|
// src/proxy.ts
|
|
5937
|
+
var DEFAULT_STARTUP_TIMEOUT_MS = 1e4;
|
|
5793
5938
|
async function runMcpStdioProxy(options = {}) {
|
|
5794
5939
|
const input = options.input ?? process.stdin;
|
|
5795
5940
|
const output = options.output ?? process.stdout;
|
|
5796
|
-
const
|
|
5797
|
-
const
|
|
5798
|
-
const
|
|
5799
|
-
const
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5941
|
+
const stderr = options.stderr ?? process.stderr;
|
|
5942
|
+
const startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
5943
|
+
const lifecycleController = new AbortController;
|
|
5944
|
+
const abortFromInput = () => lifecycleController.abort();
|
|
5945
|
+
let startupExpired = false;
|
|
5946
|
+
let startupTimer;
|
|
5947
|
+
const clearStartupWatchdog = () => {
|
|
5948
|
+
if (startupTimer === undefined)
|
|
5949
|
+
return;
|
|
5950
|
+
clearTimeout(startupTimer);
|
|
5951
|
+
startupTimer = undefined;
|
|
5804
5952
|
};
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
onHandlerError: (error) => {
|
|
5814
|
-
process.stderr.write(`[lsp-daemon] proxy error: ${error instanceof Error ? error.message : String(error)}
|
|
5953
|
+
input.once("end", abortFromInput);
|
|
5954
|
+
input.once("close", abortFromInput);
|
|
5955
|
+
if (startupTimeoutMs > 0) {
|
|
5956
|
+
startupTimer = setTimeout(() => {
|
|
5957
|
+
startupExpired = true;
|
|
5958
|
+
input.destroy();
|
|
5959
|
+
try {
|
|
5960
|
+
stderr.write(`[lsp-daemon] no MCP request received within ${startupTimeoutMs}ms; exiting
|
|
5815
5961
|
`);
|
|
5816
|
-
|
|
5817
|
-
|
|
5962
|
+
} catch (error) {
|
|
5963
|
+
if (stderr !== process.stderr) {
|
|
5964
|
+
process.stderr.write(`[lsp-daemon] startup diagnostic failed: ${error instanceof Error ? error.message : String(error)}
|
|
5965
|
+
`);
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
}, startupTimeoutMs);
|
|
5969
|
+
}
|
|
5970
|
+
try {
|
|
5971
|
+
const paths = options.paths ?? daemonPaths();
|
|
5972
|
+
const env = options.env ?? process.env;
|
|
5973
|
+
const cwd = options.cwd ?? inferOpenCodeProjectCwd(env["LSP_TOOLS_MCP_PROJECT_CONFIG"]);
|
|
5974
|
+
const contextEnv = cwd === undefined ? env : canonicalizeContextEnv(env);
|
|
5975
|
+
const contextInput = {
|
|
5976
|
+
env: contextEnv,
|
|
5977
|
+
...cwd === undefined ? {} : { cwd },
|
|
5978
|
+
...options.homeDir === undefined ? {} : { homeDir: options.homeDir }
|
|
5979
|
+
};
|
|
5980
|
+
const context = options.context ?? createStandaloneMcpRequestContext(contextInput);
|
|
5981
|
+
const callOptions = {
|
|
5982
|
+
paths,
|
|
5983
|
+
context,
|
|
5984
|
+
...options.ensure ? { ensure: options.ensure } : {},
|
|
5985
|
+
signal: lifecycleController.signal
|
|
5986
|
+
};
|
|
5987
|
+
await runJsonRpcStdioServer({
|
|
5988
|
+
input,
|
|
5989
|
+
output,
|
|
5990
|
+
idleTimeoutMs: 0,
|
|
5991
|
+
handler: (request, requestOptions) => {
|
|
5992
|
+
clearStartupWatchdog();
|
|
5993
|
+
return runWithRequestContext(context, () => handleProxyRequest(request, requestOptions));
|
|
5994
|
+
},
|
|
5995
|
+
handlerOptions: callOptions,
|
|
5996
|
+
onHandlerError: (error) => {
|
|
5997
|
+
stderr.write(`[lsp-daemon] proxy error: ${error instanceof Error ? error.message : String(error)}
|
|
5998
|
+
`);
|
|
5999
|
+
}
|
|
6000
|
+
});
|
|
6001
|
+
} catch (error) {
|
|
6002
|
+
if (!startupExpired || !isPrematureCloseError(error))
|
|
6003
|
+
throw error;
|
|
6004
|
+
} finally {
|
|
6005
|
+
if (startupTimer !== undefined)
|
|
6006
|
+
clearTimeout(startupTimer);
|
|
6007
|
+
input.removeListener("end", abortFromInput);
|
|
6008
|
+
input.removeListener("close", abortFromInput);
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
function isPrematureCloseError(error) {
|
|
6012
|
+
return error instanceof Error && Reflect.get(error, "code") === "ERR_STREAM_PREMATURE_CLOSE";
|
|
5818
6013
|
}
|
|
5819
6014
|
async function handleProxyRequest(parsed, callOptions) {
|
|
5820
6015
|
const toolCall = asToolCall(parsed);
|
|
5821
6016
|
if (!toolCall)
|
|
5822
6017
|
return handleLspMcpRequest(parsed);
|
|
5823
6018
|
const result = await callToolViaDaemon(toolCall.name, toolCall.args, callOptions);
|
|
5824
|
-
return successResponse(toolCall.id, {
|
|
6019
|
+
return successResponse(toolCall.id, {
|
|
6020
|
+
content: result.content,
|
|
6021
|
+
isError: result.isError ?? false,
|
|
6022
|
+
details: result.details
|
|
6023
|
+
});
|
|
5825
6024
|
}
|
|
5826
6025
|
function asToolCall(parsed) {
|
|
5827
6026
|
if (!isPlainRecord(parsed) || parsed["method"] !== "tools/call")
|