mstro-app 0.5.1 → 0.5.6
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/PRIVACY.md +9 -9
- package/README.md +71 -28
- package/bin/commands/config.js +1 -1
- package/bin/mstro.js +55 -4
- package/dist/server/cli/eta-estimator.d.ts +55 -0
- package/dist/server/cli/eta-estimator.d.ts.map +1 -0
- package/dist/server/cli/eta-estimator.js +222 -0
- package/dist/server/cli/eta-estimator.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-process.js +9 -1
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
- package/dist/server/cli/headless/mcp-config.d.ts +22 -5
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +7 -5
- package/dist/server/cli/headless/mcp-config.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +19 -0
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts +50 -0
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +64 -9
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts +21 -0
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +19 -12
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +16 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-history-store.d.ts.map +1 -1
- package/dist/server/cli/improvisation-history-store.js +5 -1
- package/dist/server/cli/improvisation-history-store.js.map +1 -1
- package/dist/server/cli/improvisation-output-queue.d.ts +5 -1
- package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -1
- package/dist/server/cli/improvisation-output-queue.js +30 -7
- package/dist/server/cli/improvisation-output-queue.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +35 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +58 -1
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/cli/improvisation-types.d.ts +9 -0
- package/dist/server/cli/improvisation-types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-types.js.map +1 -1
- package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-runner-factory.js +1 -0
- package/dist/server/cli/retry/retry-runner-factory.js.map +1 -1
- package/dist/server/engines/EngineEvent.d.ts +126 -0
- package/dist/server/engines/EngineEvent.d.ts.map +1 -0
- package/dist/server/engines/EngineEvent.js +11 -0
- package/dist/server/engines/EngineEvent.js.map +1 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.d.ts +47 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.d.ts.map +1 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js +338 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js.map +1 -0
- package/dist/server/engines/factory.d.ts +21 -0
- package/dist/server/engines/factory.d.ts.map +1 -0
- package/dist/server/engines/factory.js +152 -0
- package/dist/server/engines/factory.js.map +1 -0
- package/dist/server/engines/opencode/OpenCodeEngine.d.ts +148 -0
- package/dist/server/engines/opencode/OpenCodeEngine.d.ts.map +1 -0
- package/dist/server/engines/opencode/OpenCodeEngine.js +630 -0
- package/dist/server/engines/opencode/OpenCodeEngine.js.map +1 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.d.ts +172 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.d.ts.map +1 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.js +390 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.js.map +1 -0
- package/dist/server/engines/opencode/model-catalog.d.ts +94 -0
- package/dist/server/engines/opencode/model-catalog.d.ts.map +1 -0
- package/dist/server/engines/opencode/model-catalog.js +141 -0
- package/dist/server/engines/opencode/model-catalog.js.map +1 -0
- package/dist/server/engines/types.d.ts +146 -0
- package/dist/server/engines/types.d.ts.map +1 -0
- package/dist/server/engines/types.js +4 -0
- package/dist/server/engines/types.js.map +1 -0
- package/dist/server/index.js +9 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-haiku.d.ts +17 -4
- package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-haiku.js +8 -124
- package/dist/server/mcp/bouncer-haiku.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts +45 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +69 -5
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/classifier/BouncerClassifier.d.ts +34 -0
- package/dist/server/mcp/classifier/BouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js +4 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.d.ts +17 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +142 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.d.ts +68 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.js +182 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/factory.d.ts +70 -0
- package/dist/server/mcp/classifier/factory.d.ts.map +1 -0
- package/dist/server/mcp/classifier/factory.js +155 -0
- package/dist/server/mcp/classifier/factory.js.map +1 -0
- package/dist/server/mcp/server.js +52 -0
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/routes/index.d.ts +1 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +1 -0
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/internal.d.ts +16 -0
- package/dist/server/routes/internal.d.ts.map +1 -0
- package/dist/server/routes/internal.js +94 -0
- package/dist/server/routes/internal.js.map +1 -0
- package/dist/server/services/plan/agent-resolver.d.ts +26 -0
- package/dist/server/services/plan/agent-resolver.d.ts.map +1 -0
- package/dist/server/services/plan/agent-resolver.js +102 -0
- package/dist/server/services/plan/agent-resolver.js.map +1 -0
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +59 -11
- package/dist/server/services/plan/composer.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +3 -1
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.js +33 -1
- package/dist/server/services/plan/issue-prompt-builder.js.map +1 -1
- package/dist/server/services/plan/parser-core.d.ts.map +1 -1
- package/dist/server/services/plan/parser-core.js +1 -0
- package/dist/server/services/plan/parser-core.js.map +1 -1
- package/dist/server/services/plan/types.d.ts +1 -0
- package/dist/server/services/plan/types.d.ts.map +1 -1
- package/dist/server/services/runtime-info.d.ts +3 -0
- package/dist/server/services/runtime-info.d.ts.map +1 -0
- package/dist/server/services/runtime-info.js +21 -0
- package/dist/server/services/runtime-info.js.map +1 -0
- package/dist/server/services/settings.d.ts +76 -2
- package/dist/server/services/settings.d.ts.map +1 -1
- package/dist/server/services/settings.js +127 -4
- package/dist/server/services/settings.js.map +1 -1
- package/dist/server/services/websocket/ask-user-question-bridge.d.ts +32 -0
- package/dist/server/services/websocket/ask-user-question-bridge.d.ts.map +1 -0
- package/dist/server/services/websocket/ask-user-question-bridge.js +115 -0
- package/dist/server/services/websocket/ask-user-question-bridge.js.map +1 -0
- package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.js +19 -6
- package/dist/server/services/websocket/git-branch-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +25 -1
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +84 -2
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-complexity.js +78 -26
- package/dist/server/services/websocket/quality-complexity.js.map +1 -1
- package/dist/server/services/websocket/quality-eta.d.ts +47 -0
- package/dist/server/services/websocket/quality-eta.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-eta.js +110 -0
- package/dist/server/services/websocket/quality-eta.js.map +1 -0
- package/dist/server/services/websocket/quality-grading.d.ts +27 -4
- package/dist/server/services/websocket/quality-grading.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-grading.js +369 -201
- package/dist/server/services/websocket/quality-grading.js.map +1 -1
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-handlers.js +145 -7
- package/dist/server/services/websocket/quality-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-operations.d.ts +34 -0
- package/dist/server/services/websocket/quality-operations.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-operations.js +47 -0
- package/dist/server/services/websocket/quality-operations.js.map +1 -0
- package/dist/server/services/websocket/quality-persistence.d.ts +9 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-persistence.js +10 -0
- package/dist/server/services/websocket/quality-persistence.js.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.js +105 -56
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
- package/dist/server/services/websocket/quality-service.d.ts +9 -1
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-service.js +334 -14
- package/dist/server/services/websocket/quality-service.js.map +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts +21 -0
- package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-tools.js +49 -0
- package/dist/server/services/websocket/quality-tools.js.map +1 -1
- package/dist/server/services/websocket/quality-types.d.ts +35 -2
- package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-types.js +1 -1
- package/dist/server/services/websocket/quality-types.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +3 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +60 -9
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/session-history.js +3 -0
- package/dist/server/services/websocket/session-history.js.map +1 -1
- package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
- package/dist/server/services/websocket/session-initialization.js +158 -42
- package/dist/server/services/websocket/session-initialization.js.map +1 -1
- package/dist/server/services/websocket/session-registry.d.ts +25 -0
- package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
- package/dist/server/services/websocket/session-registry.js +19 -0
- package/dist/server/services/websocket/session-registry.js.map +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/settings-handlers.js +35 -4
- package/dist/server/services/websocket/settings-handlers.js.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.d.ts +7 -2
- package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.js +10 -2
- package/dist/server/services/websocket/tab-broadcast.js.map +1 -1
- package/dist/server/services/websocket/tab-event-buffer.d.ts +97 -8
- package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-event-buffer.js +138 -12
- package/dist/server/services/websocket/tab-event-buffer.js.map +1 -1
- package/dist/server/services/websocket/tab-event-replay.d.ts +29 -13
- package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-event-replay.js +55 -2
- package/dist/server/services/websocket/tab-event-replay.js.map +1 -1
- package/dist/server/services/websocket/tab-handlers.d.ts +9 -1
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js +47 -2
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +67 -7
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/services/websocket/types.js +12 -6
- package/dist/server/services/websocket/types.js.map +1 -1
- package/package.json +5 -3
- package/server/cli/eta-estimator.ts +249 -0
- package/server/cli/headless/claude-invoker-process.ts +9 -1
- package/server/cli/headless/mcp-config.ts +30 -5
- package/server/cli/headless/runner.ts +21 -0
- package/server/cli/headless/stall-assessor.ts +93 -0
- package/server/cli/headless/tool-watchdog.ts +21 -0
- package/server/cli/headless/types.ts +16 -1
- package/server/cli/improvisation-history-store.ts +4 -1
- package/server/cli/improvisation-output-queue.ts +29 -7
- package/server/cli/improvisation-session-manager.ts +63 -1
- package/server/cli/improvisation-types.ts +9 -0
- package/server/cli/retry/retry-runner-factory.ts +1 -0
- package/server/engines/EngineEvent.ts +156 -0
- package/server/engines/claude/ClaudeCodeEngine.ts +404 -0
- package/server/engines/factory.ts +176 -0
- package/server/engines/opencode/OpenCodeEngine.ts +786 -0
- package/server/engines/opencode/OpenCodeServerManager.ts +577 -0
- package/server/engines/opencode/model-catalog.ts +217 -0
- package/server/engines/types.ts +173 -0
- package/server/index.ts +9 -1
- package/server/mcp/bouncer-haiku.ts +21 -145
- package/server/mcp/bouncer-integration.ts +107 -5
- package/server/mcp/classifier/BouncerClassifier.ts +40 -0
- package/server/mcp/classifier/ClaudeBouncerClassifier.ts +189 -0
- package/server/mcp/classifier/OpenCodeBouncerClassifier.ts +305 -0
- package/server/mcp/classifier/factory.ts +195 -0
- package/server/mcp/server.ts +57 -0
- package/server/routes/index.ts +1 -0
- package/server/routes/internal.ts +112 -0
- package/server/services/plan/agent-resolver.ts +115 -0
- package/server/services/plan/agents/code-review.md +38 -8
- package/server/services/plan/composer.ts +63 -11
- package/server/services/plan/executor.ts +3 -1
- package/server/services/plan/issue-prompt-builder.ts +39 -1
- package/server/services/plan/parser-core.ts +1 -0
- package/server/services/plan/types.ts +4 -0
- package/server/services/runtime-info.ts +24 -0
- package/server/services/settings.ts +161 -4
- package/server/services/websocket/ask-user-question-bridge.ts +148 -0
- package/server/services/websocket/git-branch-handlers.ts +20 -6
- package/server/services/websocket/handler.ts +89 -2
- package/server/services/websocket/quality-complexity.ts +80 -26
- package/server/services/websocket/quality-eta.ts +155 -0
- package/server/services/websocket/quality-grading.ts +445 -222
- package/server/services/websocket/quality-handlers.ts +153 -7
- package/server/services/websocket/quality-operations.ts +72 -0
- package/server/services/websocket/quality-persistence.ts +17 -0
- package/server/services/websocket/quality-review-agent.ts +154 -64
- package/server/services/websocket/quality-service.ts +361 -13
- package/server/services/websocket/quality-tools.ts +51 -0
- package/server/services/websocket/quality-types.ts +41 -2
- package/server/services/websocket/session-handlers.ts +67 -10
- package/server/services/websocket/session-history.ts +3 -0
- package/server/services/websocket/session-initialization.ts +189 -46
- package/server/services/websocket/session-registry.ts +37 -0
- package/server/services/websocket/settings-handlers.ts +41 -4
- package/server/services/websocket/tab-broadcast.ts +10 -2
- package/server/services/websocket/tab-event-buffer.ts +143 -11
- package/server/services/websocket/tab-event-replay.ts +70 -3
- package/server/services/websocket/tab-handlers.ts +53 -5
- package/server/services/websocket/types.ts +85 -7
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Engine factory — returns a concrete CodingAgentEngine for the requested
|
|
5
|
+
* EngineId. Callers pass the value of `settings.engine` and the factory
|
|
6
|
+
* dispatches to the matching implementation:
|
|
7
|
+
*
|
|
8
|
+
* - 'claude-code' → ClaudeCodeEngine (headless runner, stdout JSON)
|
|
9
|
+
* - 'opencode' → OpenCodeEngine (OpenCode SDK + SSE)
|
|
10
|
+
*
|
|
11
|
+
* The OpenCode path is backed by a single process-lifetime
|
|
12
|
+
* OpenCodeServerManager that owns the `opencode serve` subprocess. The
|
|
13
|
+
* factory itself stays synchronous; manager startup is awaited inside
|
|
14
|
+
* `startSession` via `LazyOpenCodeEngine` (below) so callers observe the
|
|
15
|
+
* same lifecycle as ClaudeCodeEngine.
|
|
16
|
+
*/
|
|
17
|
+
import { isEngineSwapEnabled } from '../services/settings.js';
|
|
18
|
+
import { ClaudeCodeEngine } from './claude/ClaudeCodeEngine.js';
|
|
19
|
+
import { OpenCodeEngine } from './opencode/OpenCodeEngine.js';
|
|
20
|
+
import { OpenCodeServerManager } from './opencode/OpenCodeServerManager.js';
|
|
21
|
+
/**
|
|
22
|
+
* Process-lifetime singleton for the `opencode serve` subprocess. Created
|
|
23
|
+
* lazily on the first request for an opencode engine so Claude-only
|
|
24
|
+
* deployments never spawn the binary. `registerProcessHandlers` is set so
|
|
25
|
+
* the subprocess exits with the CLI — no orphan processes on SIGINT.
|
|
26
|
+
*/
|
|
27
|
+
let sharedOpenCodeManager = null;
|
|
28
|
+
function getSharedOpenCodeServerManager() {
|
|
29
|
+
if (!sharedOpenCodeManager) {
|
|
30
|
+
sharedOpenCodeManager = new OpenCodeServerManager({
|
|
31
|
+
registerProcessHandlers: true,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return sharedOpenCodeManager;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reset the cached OpenCode manager. Primarily for tests — never called
|
|
38
|
+
* by production code. Does not shut down the previous manager; callers
|
|
39
|
+
* that need a clean state should `shutdown()` first.
|
|
40
|
+
*/
|
|
41
|
+
export function __resetSharedOpenCodeServerManagerForTests() {
|
|
42
|
+
sharedOpenCodeManager = null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Thin adapter that defers `OpenCodeEngine` construction until
|
|
46
|
+
* `startSession` runs. The real engine requires an already-bound
|
|
47
|
+
* `OpencodeClient`, but the underlying HTTP server is spawned
|
|
48
|
+
* asynchronously by `OpenCodeServerManager`. `startSession` is the first
|
|
49
|
+
* async call on the engine lifecycle, so we await `manager.start()`
|
|
50
|
+
* there, then construct the inner engine once the client is available
|
|
51
|
+
* and forward every subsequent call to it.
|
|
52
|
+
*
|
|
53
|
+
* The wrapper preserves the public `CodingAgentEngine` contract:
|
|
54
|
+
* - `engineId` is stable at `'opencode'` from construction.
|
|
55
|
+
* - Methods called before `startSession` resolve reject with the same
|
|
56
|
+
* error wording the inner engine would have produced.
|
|
57
|
+
* - `dispose()` is idempotent and tolerates the uninitialized case.
|
|
58
|
+
*/
|
|
59
|
+
class LazyOpenCodeEngine {
|
|
60
|
+
manager;
|
|
61
|
+
engineId = 'opencode';
|
|
62
|
+
inner = null;
|
|
63
|
+
started = false;
|
|
64
|
+
disposed = false;
|
|
65
|
+
constructor(manager) {
|
|
66
|
+
this.manager = manager;
|
|
67
|
+
}
|
|
68
|
+
async startSession(options) {
|
|
69
|
+
if (this.disposed) {
|
|
70
|
+
throw new Error('OpenCodeEngine: cannot start a disposed engine');
|
|
71
|
+
}
|
|
72
|
+
if (this.started) {
|
|
73
|
+
throw new Error('OpenCodeEngine: startSession called more than once');
|
|
74
|
+
}
|
|
75
|
+
await this.manager.start();
|
|
76
|
+
const client = this.manager.getClient();
|
|
77
|
+
this.inner = new OpenCodeEngine({
|
|
78
|
+
client,
|
|
79
|
+
directory: options.workingDir,
|
|
80
|
+
});
|
|
81
|
+
await this.inner.startSession(options);
|
|
82
|
+
this.started = true;
|
|
83
|
+
}
|
|
84
|
+
sendPrompt(prompt, attachments) {
|
|
85
|
+
if (this.disposed) {
|
|
86
|
+
return Promise.reject(new Error('OpenCodeEngine: sendPrompt called after dispose'));
|
|
87
|
+
}
|
|
88
|
+
if (!this.inner) {
|
|
89
|
+
return Promise.reject(new Error('OpenCodeEngine: sendPrompt called before startSession'));
|
|
90
|
+
}
|
|
91
|
+
return this.inner.sendPrompt(prompt, attachments);
|
|
92
|
+
}
|
|
93
|
+
cancel() {
|
|
94
|
+
if (!this.inner)
|
|
95
|
+
return Promise.resolve();
|
|
96
|
+
return this.inner.cancel();
|
|
97
|
+
}
|
|
98
|
+
getUsage() {
|
|
99
|
+
if (!this.inner) {
|
|
100
|
+
return { inputTokens: 0, outputTokens: 0, lastUpdatedAt: Date.now() };
|
|
101
|
+
}
|
|
102
|
+
return this.inner.getUsage();
|
|
103
|
+
}
|
|
104
|
+
async dispose() {
|
|
105
|
+
if (this.disposed)
|
|
106
|
+
return;
|
|
107
|
+
this.disposed = true;
|
|
108
|
+
if (this.inner) {
|
|
109
|
+
await this.inner.dispose();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
[Symbol.asyncIterator]() {
|
|
113
|
+
// The contract only allows iteration after `startSession` has resolved,
|
|
114
|
+
// so `this.inner` is guaranteed to be set when consumers begin the
|
|
115
|
+
// for-await loop. We delegate directly to the inner engine's iterator
|
|
116
|
+
// to preserve its ordering and terminal-event semantics.
|
|
117
|
+
if (!this.inner) {
|
|
118
|
+
return {
|
|
119
|
+
next: () => Promise.resolve({ value: undefined, done: true }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return this.inner[Symbol.asyncIterator]();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Construct a new engine instance for the given engine id. The returned
|
|
127
|
+
* engine is uninitialized — the caller must call `startSession` before
|
|
128
|
+
* any other method.
|
|
129
|
+
*
|
|
130
|
+
* Feature-flag gate: when `engineSwap` is disabled, this returns
|
|
131
|
+
* `ClaudeCodeEngine` for every id. That guarantees the pre-OpenCode
|
|
132
|
+
* behavior — in particular, `LazyOpenCodeEngine` is never constructed, so
|
|
133
|
+
* the shared `OpenCodeServerManager` is never touched and no `opencode
|
|
134
|
+
* serve` subprocess is spawned. The flag is checked on every call (rather
|
|
135
|
+
* than cached) so runtime toggles take effect on the next session start.
|
|
136
|
+
*/
|
|
137
|
+
export function createEngine(engineId) {
|
|
138
|
+
if (!isEngineSwapEnabled()) {
|
|
139
|
+
return new ClaudeCodeEngine();
|
|
140
|
+
}
|
|
141
|
+
switch (engineId) {
|
|
142
|
+
case 'claude-code':
|
|
143
|
+
return new ClaudeCodeEngine();
|
|
144
|
+
case 'opencode':
|
|
145
|
+
return new LazyOpenCodeEngine(getSharedOpenCodeServerManager());
|
|
146
|
+
default: {
|
|
147
|
+
const exhaustive = engineId;
|
|
148
|
+
throw new Error(`Unknown engine id: ${String(exhaustive)}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../server/engines/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCodeEngine
|
|
3
|
+
*
|
|
4
|
+
* Adapter that wraps the OpenCode SDK (@opencode-ai/sdk) behind the
|
|
5
|
+
* CodingAgentEngine interface. Owns a single OpenCode `session` per Mstro
|
|
6
|
+
* improvisation:
|
|
7
|
+
*
|
|
8
|
+
* - `startSession` creates (or resumes) an OpenCode session and opens an
|
|
9
|
+
* SSE subscription to `/event`. A background pump consumes the stream
|
|
10
|
+
* and translates each payload into the engine-agnostic `EngineEvent`
|
|
11
|
+
* shape so the rest of the system does not need to know which engine
|
|
12
|
+
* produced the events.
|
|
13
|
+
* - `sendPrompt` dispatches `session.promptAsync` and resolves as soon as
|
|
14
|
+
* the server has accepted the prompt. Streaming output arrives via SSE.
|
|
15
|
+
* - `cancel` calls `session.abort`, which the OpenCode server eventually
|
|
16
|
+
* reflects back as a `session.idle` event.
|
|
17
|
+
* - `dispose` stops the pump and deletes the underlying session.
|
|
18
|
+
*
|
|
19
|
+
* The concrete SSE → EngineEvent mapping is documented inline in
|
|
20
|
+
* `handleSseEvent`.
|
|
21
|
+
*/
|
|
22
|
+
import type { OpencodeClient } from '@opencode-ai/sdk';
|
|
23
|
+
import { type BouncerDecision, type EnginePermissionReviewRequest } from '../../mcp/bouncer-integration.js';
|
|
24
|
+
import type { EngineEvent, EngineId } from '../EngineEvent.js';
|
|
25
|
+
import type { CodingAgentEngine, EngineUsage, PromptAttachment, StartSessionOptions } from '../types.js';
|
|
26
|
+
/**
|
|
27
|
+
* Bouncer entry-point signature. Exposed as an option on OpenCodeEngine so
|
|
28
|
+
* tests can swap in a stub without patching module internals. Production
|
|
29
|
+
* code passes `reviewEnginePermission` from bouncer-integration.ts, which
|
|
30
|
+
* in turn calls {@link reviewOperation} — the single source of truth for
|
|
31
|
+
* security decisions across every engine.
|
|
32
|
+
*/
|
|
33
|
+
export type ReviewEnginePermissionFn = (request: EnginePermissionReviewRequest) => Promise<BouncerDecision>;
|
|
34
|
+
/** Construction-time dependencies for {@link OpenCodeEngine}. */
|
|
35
|
+
export interface OpenCodeEngineOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Typed SDK client, already bound to a running opencode server. Usually
|
|
38
|
+
* obtained from `OpenCodeServerManager.getClient()`. Tests inject a
|
|
39
|
+
* hand-rolled mock matching the subset of methods used here.
|
|
40
|
+
*/
|
|
41
|
+
client: OpencodeClient;
|
|
42
|
+
/**
|
|
43
|
+
* Directory query parameter forwarded to each request. OpenCode scopes
|
|
44
|
+
* sessions and events by directory — the value is typically the same
|
|
45
|
+
* working directory passed to `startSession`.
|
|
46
|
+
*/
|
|
47
|
+
directory?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Override the bouncer review function. Defaults to
|
|
50
|
+
* `reviewEnginePermission` from `cli/server/mcp/bouncer-integration.ts`
|
|
51
|
+
* — which wraps the unified {@link reviewOperation} entry point used by
|
|
52
|
+
* the Claude MCP path. Tests inject a stub to drive specific decisions.
|
|
53
|
+
*/
|
|
54
|
+
reviewPermission?: ReviewEnginePermissionFn;
|
|
55
|
+
}
|
|
56
|
+
export declare class OpenCodeEngine implements CodingAgentEngine {
|
|
57
|
+
readonly engineId: EngineId;
|
|
58
|
+
private readonly client;
|
|
59
|
+
private readonly directory;
|
|
60
|
+
private readonly reviewPermission;
|
|
61
|
+
private sessionOptions;
|
|
62
|
+
private openCodeSessionId;
|
|
63
|
+
/** True once the caller has called `startSession` successfully. */
|
|
64
|
+
private started;
|
|
65
|
+
/** Active SSE subscription returned by `client.event.subscribe()`. */
|
|
66
|
+
private subscription;
|
|
67
|
+
/** Background task consuming `subscription.stream`. */
|
|
68
|
+
private pumpPromise;
|
|
69
|
+
/** In-flight prompt promise — enforces the one-prompt-at-a-time contract. */
|
|
70
|
+
private currentPromptPromise;
|
|
71
|
+
private disposed;
|
|
72
|
+
private iteratorDone;
|
|
73
|
+
private readonly queue;
|
|
74
|
+
private readonly pending;
|
|
75
|
+
/**
|
|
76
|
+
* Tool-call id → start timestamp. Populated on the first `running`
|
|
77
|
+
* state of a ToolPart so we can compute `durationMs` when the part
|
|
78
|
+
* transitions to `completed` or `error`.
|
|
79
|
+
*/
|
|
80
|
+
private readonly toolStartTimes;
|
|
81
|
+
/**
|
|
82
|
+
* Tool-call ids we have already announced via `tool.start`. Prevents
|
|
83
|
+
* duplicate starts when OpenCode emits multiple `running` updates.
|
|
84
|
+
*/
|
|
85
|
+
private readonly toolStarted;
|
|
86
|
+
private usage;
|
|
87
|
+
constructor(options: OpenCodeEngineOptions);
|
|
88
|
+
startSession(options: StartSessionOptions): Promise<void>;
|
|
89
|
+
sendPrompt(prompt: string, _attachments?: PromptAttachment[]): Promise<void>;
|
|
90
|
+
cancel(): Promise<void>;
|
|
91
|
+
getUsage(): EngineUsage;
|
|
92
|
+
dispose(): Promise<void>;
|
|
93
|
+
[Symbol.asyncIterator](): AsyncIterator<EngineEvent>;
|
|
94
|
+
private sessionIdForEvent;
|
|
95
|
+
private emit;
|
|
96
|
+
private closeIterator;
|
|
97
|
+
/**
|
|
98
|
+
* Long-running task that consumes the SSE stream. Exits when the stream
|
|
99
|
+
* ends naturally (dispose called `stream.return()`) or when an error
|
|
100
|
+
* propagates out of the generator.
|
|
101
|
+
*/
|
|
102
|
+
private runEventPump;
|
|
103
|
+
/**
|
|
104
|
+
* Core mapping from OpenCode SSE events to EngineEvents.
|
|
105
|
+
*
|
|
106
|
+
* - message.part.updated (TextPart) → message.delta
|
|
107
|
+
* - message.part.updated (ReasoningPart) → message.thinking
|
|
108
|
+
* - message.part.updated (ToolPart running) → tool.start (once per callID)
|
|
109
|
+
* - message.part.updated (ToolPart done) → tool.end
|
|
110
|
+
* - message.part.updated (StepFinishPart) → usage.update
|
|
111
|
+
* - message.updated (AssistantMessage) → usage.update (if tokens set)
|
|
112
|
+
* - permission.updated → permission.request
|
|
113
|
+
* - session.idle → session.idle
|
|
114
|
+
* - session.error → engine.error
|
|
115
|
+
*
|
|
116
|
+
* Events for sessions other than the one we own are ignored so that a
|
|
117
|
+
* shared server emitting events for multiple clients does not cross
|
|
118
|
+
* streams.
|
|
119
|
+
*/
|
|
120
|
+
private handleSseEvent;
|
|
121
|
+
/**
|
|
122
|
+
* Route an OpenCode `permission.updated` through the unified Bouncer
|
|
123
|
+
* and respond to the SDK so the server never hangs waiting.
|
|
124
|
+
*
|
|
125
|
+
* Contract:
|
|
126
|
+
* - Approval (allow / warn_allow) → SDK `{ response: 'once' }`.
|
|
127
|
+
* - Denial (deny) → SDK `{ response: 'reject' }` *and*
|
|
128
|
+
* a user-visible `engine.error` carrying the same message the Claude
|
|
129
|
+
* MCP path returns on a deny (see `cli/server/mcp/server.ts`), so
|
|
130
|
+
* both engines surface denials with identical wording.
|
|
131
|
+
*
|
|
132
|
+
* Any error — bouncer failure, SDK failure — is treated as a deny for
|
|
133
|
+
* safety: we tell OpenCode `reject`, emit an engine.error, and keep the
|
|
134
|
+
* session alive (non-fatal).
|
|
135
|
+
*/
|
|
136
|
+
private resolvePermission;
|
|
137
|
+
private reviewPermissionSafely;
|
|
138
|
+
private handlePartUpdated;
|
|
139
|
+
private onTextPart;
|
|
140
|
+
private onReasoningPart;
|
|
141
|
+
private onToolPart;
|
|
142
|
+
private emitToolStartOnce;
|
|
143
|
+
private emitToolEnd;
|
|
144
|
+
private onStepFinish;
|
|
145
|
+
private handleMessageUpdated;
|
|
146
|
+
private applyTokens;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=OpenCodeEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenCodeEngine.d.ts","sourceRoot":"","sources":["../../../../server/engines/opencode/OpenCodeEngine.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAGV,cAAc,EAQf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,6BAA6B,EAGnC,MAAM,kCAAkC,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,aAAa,CAAA;AAEpB;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,CACrC,OAAO,EAAE,6BAA6B,KACnC,OAAO,CAAC,eAAe,CAAC,CAAA;AAc7B,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,MAAM,EAAE,cAAc,CAAA;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,wBAAwB,CAAA;CAC5C;AAqBD,qBAAa,cAAe,YAAW,iBAAiB;IACtD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAa;IAExC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IAE3D,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,mEAAmE;IACnE,OAAO,CAAC,OAAO,CAAQ;IAEvB,sEAAsE;IACtE,OAAO,CAAC,YAAY,CAAiC;IACrD,uDAAuD;IACvD,OAAO,CAAC,WAAW,CAA6B;IAEhD,6EAA6E;IAC7E,OAAO,CAAC,oBAAoB,CAA6B;IAEzD,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IAEzC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IAErD,OAAO,CAAC,KAAK,CAIZ;gBAEW,OAAO,EAAE,qBAAqB;IASpC,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCzD,UAAU,CACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,gBAAgB,EAAE,GAChC,OAAO,CAAC,IAAI,CAAC;IAmCV,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB7B,QAAQ,IAAI,WAAW;IAIjB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC9B,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,WAAW,CAAC;IAyBpD,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,IAAI;IAaZ,OAAO,CAAC,aAAa;IASrB;;;;OAIG;YACW,YAAY;IAsB1B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,cAAc;IAmEtB;;;;;;;;;;;;;;OAcG;YACW,iBAAiB;YA4CjB,sBAAsB;IAyBpC,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,WAAW;CA0CpB"}
|