@getpaseo/server 0.1.73 → 0.1.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scripts/supervisor-entrypoint.js +2 -21
- package/dist/scripts/supervisor-entrypoint.js.map +1 -1
- package/dist/scripts/supervisor-log-config.js +31 -0
- package/dist/scripts/supervisor-log-config.js.map +1 -0
- package/dist/server/server/agent/agent-manager.d.ts +4 -1
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +139 -14
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-prompt.d.ts.map +1 -1
- package/dist/server/server/agent/agent-prompt.js +27 -0
- package/dist/server/server/agent/agent-prompt.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +2 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js +3 -0
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/foreground-run-state.d.ts +1 -1
- package/dist/server/server/agent/foreground-run-state.d.ts.map +1 -1
- package/dist/server/server/agent/foreground-run-state.js +2 -4
- package/dist/server/server/agent/foreground-run-state.js.map +1 -1
- package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
- package/dist/server/server/agent/provider-manifest.js +3 -3
- package/dist/server/server/agent/provider-manifest.js.map +1 -1
- package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
- package/dist/server/server/agent/provider-registry.js +13 -5
- package/dist/server/server/agent/provider-registry.js.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts +39 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.js +121 -14
- package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/claude/agent.d.ts +1 -1
- package/dist/server/server/agent/providers/claude/agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/agent.js +80 -24
- package/dist/server/server/agent/providers/claude/agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +7 -1
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex/app-server-transport.js +28 -1
- package/dist/server/server/agent/providers/codex/app-server-transport.js.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +36 -7
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +14 -4
- 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 +121 -15
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +10 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +114 -7
- package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +1 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +7 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts +10 -33
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +140 -448
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.js +2 -15
- package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -1
- package/dist/server/server/agent/providers/provider-runner.d.ts +1 -1
- package/dist/server/server/agent/providers/provider-runner.d.ts.map +1 -1
- package/dist/server/server/agent/providers/provider-runner.js +2 -1
- package/dist/server/server/agent/providers/provider-runner.js.map +1 -1
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +1 -1
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts.map +1 -1
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +2 -1
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js.map +1 -1
- package/dist/server/server/agent/stt-manager.d.ts +5 -1
- package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
- package/dist/server/server/agent/stt-manager.js +3 -2
- package/dist/server/server/agent/stt-manager.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts +5 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/dictation/dictation-stream-manager.d.ts +2 -0
- package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
- package/dist/server/server/dictation/dictation-stream-manager.js +2 -1
- package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
- package/dist/server/server/logger.js +1 -1
- package/dist/server/server/logger.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +18 -0
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- package/dist/server/server/persisted-config.js +2 -0
- package/dist/server/server/persisted-config.js.map +1 -1
- package/dist/server/server/schedule/service.d.ts.map +1 -1
- package/dist/server/server/schedule/service.js +14 -1
- package/dist/server/server/schedule/service.js.map +1 -1
- package/dist/server/server/session.d.ts +3 -0
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +89 -18
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/speech/providers/local/config.d.ts +5 -0
- package/dist/server/server/speech/providers/local/config.d.ts.map +1 -1
- package/dist/server/server/speech/providers/local/config.js +35 -0
- package/dist/server/server/speech/providers/local/config.js.map +1 -1
- package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -1
- package/dist/server/server/speech/speech-config-resolver.js +1 -0
- package/dist/server/server/speech/speech-config-resolver.js.map +1 -1
- package/dist/server/server/speech/speech-runtime.d.ts +2 -0
- package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
- package/dist/server/server/speech/speech-runtime.js +2 -0
- package/dist/server/server/speech/speech-runtime.js.map +1 -1
- package/dist/server/server/voice/voice-turn-controller.d.ts +1 -0
- package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -1
- package/dist/server/server/voice/voice-turn-controller.js +1 -1
- package/dist/server/server/voice/voice-turn-controller.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +2 -0
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/src/server/agent/agent-sdk-types.js +3 -0
- package/dist/src/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/src/server/agent/provider-manifest.js +3 -3
- package/dist/src/server/agent/provider-manifest.js.map +1 -1
- package/dist/src/server/persisted-config.js +2 -0
- package/dist/src/server/persisted-config.js.map +1 -1
- package/package.json +3 -3
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { findExecutable, isCommandAvailable } from "../../../utils/executable.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
|
|
6
7
|
import { createProviderEnvSpec } from "../provider-launch-config.js";
|
|
7
8
|
import { withTimeout } from "../../../utils/promise-timeout.js";
|
|
8
9
|
import { execCommand } from "../../../utils/spawn.js";
|
|
@@ -24,22 +25,8 @@ const OPENCODE_CAPABILITIES = {
|
|
|
24
25
|
const OPENCODE_BUILD_MODE_ID = "build";
|
|
25
26
|
const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
|
|
26
27
|
const OPENCODE_STORAGE_SESSION_LIMIT = 200;
|
|
27
|
-
// COMPAT(opencodeEofRecovery): added in v0.1.73 to compensate for OpenCode 1.14.42+
|
|
28
|
-
// closing the /event SSE stream cleanly after `server.connected`. Drop this whole
|
|
29
|
-
// recovery path once OpenCode upstream restores live event delivery and the floor
|
|
30
|
-
// version reflects that.
|
|
31
|
-
// Upstream: anomalyco/opencode#26697 (SSE /event closes immediately after
|
|
32
|
-
// server.connected) and anomalyco/opencode#26635 (prompt_async silently discards
|
|
33
|
-
// requests; SSE path broken).
|
|
34
|
-
const OPENCODE_EOF_RECOVERY_TIMEOUT_MS = 5 * 60 * 1000;
|
|
35
|
-
const OPENCODE_EOF_RECOVERY_POLL_INTERVAL_MS = 1000;
|
|
36
|
-
const OPENCODE_RECOVERY_ABORT_TIMEOUT_MS = 2000;
|
|
37
28
|
const OPENCODE_PENDING_ABORT_START_TIMEOUT_MS = 10000;
|
|
38
|
-
|
|
39
|
-
// message is ever persisted. Bound the wait so the turn fails in seconds instead
|
|
40
|
-
// of hanging until the completion cap. Valid models normally persist their first
|
|
41
|
-
// message within a second of LLM start, so 10s leaves comfortable headroom.
|
|
42
|
-
const OPENCODE_EOF_RECOVERY_LIVENESS_MS = 10000;
|
|
29
|
+
const OPENCODE_RETRY_STATUS_FAILURE_MS = 10000;
|
|
43
30
|
const DEFAULT_MODES = [
|
|
44
31
|
{
|
|
45
32
|
id: OPENCODE_BUILD_MODE_ID,
|
|
@@ -451,16 +438,6 @@ function mergeOpenCodeStepFinishUsage(usage, part) {
|
|
|
451
438
|
usage.totalCostUsd = (usage.totalCostUsd ?? 0) + cost;
|
|
452
439
|
}
|
|
453
440
|
}
|
|
454
|
-
function formatOpenCodeAssistantErrorMessage(error) {
|
|
455
|
-
const data = error.data;
|
|
456
|
-
if (data && typeof data === "object" && "message" in data) {
|
|
457
|
-
const message = data.message;
|
|
458
|
-
if (typeof message === "string" && message.trim().length > 0) {
|
|
459
|
-
return message.trim();
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return error.name;
|
|
463
|
-
}
|
|
464
441
|
function hasNormalizedOpenCodeUsage(usage) {
|
|
465
442
|
return [
|
|
466
443
|
usage.inputTokens,
|
|
@@ -702,16 +679,11 @@ export class OpenCodeAgentClient {
|
|
|
702
679
|
this.logger = logger.child({ module: "agent", provider: "opencode" });
|
|
703
680
|
this.runtimeSettings = runtimeSettings;
|
|
704
681
|
this.storageRoot = storageRoot ?? resolveOpenCodeStorageRoot();
|
|
705
|
-
this.recovery = deps.recovery ?? {
|
|
706
|
-
timeoutMs: OPENCODE_EOF_RECOVERY_TIMEOUT_MS,
|
|
707
|
-
pollIntervalMs: OPENCODE_EOF_RECOVERY_POLL_INTERVAL_MS,
|
|
708
|
-
livenessMs: OPENCODE_EOF_RECOVERY_LIVENESS_MS,
|
|
709
|
-
};
|
|
710
682
|
this.runtime =
|
|
711
683
|
deps.runtime ??
|
|
712
684
|
new ProductionOpenCodeRuntime(OpenCodeServerManager.getInstance(this.logger, runtimeSettings));
|
|
713
685
|
}
|
|
714
|
-
async createSession(config,
|
|
686
|
+
async createSession(config, launchContext, options) {
|
|
715
687
|
const openCodeConfig = this.assertConfig(config);
|
|
716
688
|
const acquisition = await this.runtime.acquireServer({ force: false });
|
|
717
689
|
const { url } = acquisition.server;
|
|
@@ -729,14 +701,14 @@ export class OpenCodeAgentClient {
|
|
|
729
701
|
throw new Error("OpenCode session creation returned no data");
|
|
730
702
|
}
|
|
731
703
|
await this.populateModelContextWindowCache(client, openCodeConfig.cwd);
|
|
732
|
-
return new OpenCodeAgentSession(openCodeConfig, client, session.id, this.logger, this.storageRoot, new Map(this.modelContextWindows), acquisition.release, options?.persistSession,
|
|
704
|
+
return new OpenCodeAgentSession(openCodeConfig, client, session.id, this.logger, this.storageRoot, new Map(this.modelContextWindows), acquisition.release, options?.persistSession, launchContext?.agentId);
|
|
733
705
|
}
|
|
734
706
|
catch (error) {
|
|
735
707
|
acquisition.release();
|
|
736
708
|
throw error;
|
|
737
709
|
}
|
|
738
710
|
}
|
|
739
|
-
async resumeSession(handle, overrides,
|
|
711
|
+
async resumeSession(handle, overrides, launchContext) {
|
|
740
712
|
const cwd = overrides?.cwd ?? handle.metadata?.cwd;
|
|
741
713
|
if (!cwd) {
|
|
742
714
|
throw new Error("OpenCode resume requires the original working directory");
|
|
@@ -755,7 +727,7 @@ export class OpenCodeAgentClient {
|
|
|
755
727
|
});
|
|
756
728
|
try {
|
|
757
729
|
await this.populateModelContextWindowCache(client, openCodeConfig.cwd);
|
|
758
|
-
return new OpenCodeAgentSession(openCodeConfig, client, handle.sessionId, this.logger, this.storageRoot, new Map(this.modelContextWindows), acquisition.release, undefined,
|
|
730
|
+
return new OpenCodeAgentSession(openCodeConfig, client, handle.sessionId, this.logger, this.storageRoot, new Map(this.modelContextWindows), acquisition.release, undefined, launchContext?.agentId);
|
|
759
731
|
}
|
|
760
732
|
catch (error) {
|
|
761
733
|
acquisition.release();
|
|
@@ -1592,25 +1564,23 @@ function createDeferred() {
|
|
|
1592
1564
|
});
|
|
1593
1565
|
return { promise, resolve, reject };
|
|
1594
1566
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if (!
|
|
1598
|
-
return;
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1567
|
+
function unwrapOpenCodeGlobalEvent(event) {
|
|
1568
|
+
const record = readOpenCodeRecord(event);
|
|
1569
|
+
if (!record) {
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
const payload = readOpenCodeRecord(record.payload);
|
|
1573
|
+
if (typeof payload?.type === "string") {
|
|
1574
|
+
return payload;
|
|
1575
|
+
}
|
|
1576
|
+
if (typeof record.type === "string") {
|
|
1577
|
+
return record;
|
|
1578
|
+
}
|
|
1579
|
+
return null;
|
|
1607
1580
|
}
|
|
1608
1581
|
class OpenCodeAgentSession {
|
|
1609
|
-
constructor(config, client, sessionId, logger,
|
|
1610
|
-
|
|
1611
|
-
pollIntervalMs: OPENCODE_EOF_RECOVERY_POLL_INTERVAL_MS,
|
|
1612
|
-
livenessMs: OPENCODE_EOF_RECOVERY_LIVENESS_MS,
|
|
1613
|
-
}) {
|
|
1582
|
+
constructor(config, client, sessionId, logger, _storageRoot, modelContextWindowsByModelKey = new Map(), releaseServer, persistSession = true, agentId) {
|
|
1583
|
+
this.agentId = agentId;
|
|
1614
1584
|
this.provider = "opencode";
|
|
1615
1585
|
this.capabilities = OPENCODE_CAPABILITIES;
|
|
1616
1586
|
this.currentMode = "default";
|
|
@@ -1637,25 +1607,15 @@ class OpenCodeAgentSession {
|
|
|
1637
1607
|
this.subAgentCallIdByChildSessionId = new Map();
|
|
1638
1608
|
this.pendingChildToolPartsBySessionId = new Map();
|
|
1639
1609
|
this.deletedFromProvider = false;
|
|
1640
|
-
this.
|
|
1641
|
-
this.foregroundAssistantText = "";
|
|
1642
|
-
this.foregroundUsageUpdated = false;
|
|
1643
|
-
this.foregroundKnownMessageIds = new Set();
|
|
1644
|
-
this.foregroundEmittedQuestionIds = new Set();
|
|
1645
|
-
this.foregroundEmittedPermissionIds = new Set();
|
|
1646
|
-
this.foregroundEmittedReasoningTextLengthByPartId = new Map();
|
|
1647
|
-
this.foregroundEmittedToolCallSignatureByCallId = new Map();
|
|
1648
|
-
this.foregroundTurnStartedAt = null;
|
|
1610
|
+
this.retryFailureTimer = null;
|
|
1649
1611
|
this.config = config;
|
|
1650
1612
|
this.client = client;
|
|
1651
1613
|
this.sessionId = sessionId;
|
|
1652
|
-
this.logger = logger;
|
|
1653
|
-
this.storageRoot = storageRoot;
|
|
1614
|
+
this.logger = logger.child({ agentId: this.agentId });
|
|
1654
1615
|
this.modelContextWindowsByModelKey = modelContextWindowsByModelKey;
|
|
1655
1616
|
this.currentMode = normalizeOpenCodeModeId(config.modeId);
|
|
1656
1617
|
this.releaseServer = releaseServer ?? null;
|
|
1657
1618
|
this.persistSession = persistSession;
|
|
1658
|
-
this.recovery = recovery;
|
|
1659
1619
|
this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(config.model);
|
|
1660
1620
|
}
|
|
1661
1621
|
get id() {
|
|
@@ -1739,19 +1699,11 @@ class OpenCodeAgentSession {
|
|
|
1739
1699
|
throw new Error("A foreground turn is already active");
|
|
1740
1700
|
}
|
|
1741
1701
|
await this.awaitPendingAbortBeforeStartingTurn();
|
|
1742
|
-
this.foregroundTurnStartedAt = Date.now();
|
|
1743
1702
|
this.runningToolCalls.clear();
|
|
1744
1703
|
this.subAgentsByCallId.clear();
|
|
1745
1704
|
this.subAgentCallIdByChildSessionId.clear();
|
|
1746
1705
|
this.pendingChildToolPartsBySessionId.clear();
|
|
1747
|
-
this.
|
|
1748
|
-
this.foregroundAssistantText = "";
|
|
1749
|
-
this.foregroundUsageUpdated = false;
|
|
1750
|
-
this.foregroundEmittedQuestionIds.clear();
|
|
1751
|
-
this.foregroundEmittedPermissionIds.clear();
|
|
1752
|
-
this.foregroundEmittedReasoningTextLengthByPartId.clear();
|
|
1753
|
-
this.foregroundEmittedToolCallSignatureByCallId.clear();
|
|
1754
|
-
this.foregroundKnownMessageIds = await this.readPersistedSessionMessageIds();
|
|
1706
|
+
this.clearRetryFailureTimer();
|
|
1755
1707
|
const turnAbortController = new AbortController();
|
|
1756
1708
|
this.abortController = turnAbortController;
|
|
1757
1709
|
await this.ensureMcpServersConfigured();
|
|
@@ -1860,7 +1812,7 @@ class OpenCodeAgentSession {
|
|
|
1860
1812
|
// SDK input validation) is caught alongside async rejections. A plain
|
|
1861
1813
|
// `.then().catch()` chain would let a sync throw escape unhandled.
|
|
1862
1814
|
void (async () => {
|
|
1863
|
-
traceOpenCode("
|
|
1815
|
+
this.traceOpenCode("provider.opencode.prompt_async.start", {
|
|
1864
1816
|
turnId,
|
|
1865
1817
|
sessionId: this.sessionId,
|
|
1866
1818
|
model,
|
|
@@ -1886,7 +1838,7 @@ class OpenCodeAgentSession {
|
|
|
1886
1838
|
...(effectiveMode ? { agent: effectiveMode } : {}),
|
|
1887
1839
|
...(effectiveVariant ? { variant: effectiveVariant } : {}),
|
|
1888
1840
|
});
|
|
1889
|
-
traceOpenCode("
|
|
1841
|
+
this.traceOpenCode("provider.opencode.prompt_async.response", {
|
|
1890
1842
|
turnId,
|
|
1891
1843
|
hasError: promptResponse.error !== undefined,
|
|
1892
1844
|
error: promptResponse.error,
|
|
@@ -1901,7 +1853,7 @@ class OpenCodeAgentSession {
|
|
|
1901
1853
|
}
|
|
1902
1854
|
}
|
|
1903
1855
|
catch (error) {
|
|
1904
|
-
traceOpenCode("
|
|
1856
|
+
this.traceOpenCode("provider.opencode.prompt_async.throw", {
|
|
1905
1857
|
turnId,
|
|
1906
1858
|
error: error instanceof Error
|
|
1907
1859
|
? { name: error.name, message: error.message, stack: error.stack }
|
|
@@ -1924,66 +1876,49 @@ class OpenCodeAgentSession {
|
|
|
1924
1876
|
};
|
|
1925
1877
|
}
|
|
1926
1878
|
async consumeEventStream(turnId, turnAbortController, subscriptionReady) {
|
|
1927
|
-
traceOpenCode("subscribe.start", {
|
|
1879
|
+
this.traceOpenCode("provider.opencode.subscribe.start", {
|
|
1880
|
+
turnId,
|
|
1881
|
+
sessionId: this.sessionId,
|
|
1882
|
+
cwd: this.config.cwd,
|
|
1883
|
+
});
|
|
1928
1884
|
try {
|
|
1929
|
-
const result = await this.client.event
|
|
1930
|
-
|
|
1931
|
-
|
|
1885
|
+
const result = await this.client.global.event({
|
|
1886
|
+
signal: turnAbortController.signal,
|
|
1887
|
+
sseMaxRetryAttempts: 0,
|
|
1888
|
+
});
|
|
1932
1889
|
let eventCount = 0;
|
|
1933
|
-
|
|
1890
|
+
let subscriptionReadyResolved = false;
|
|
1891
|
+
for await (const rawEvent of result.stream) {
|
|
1934
1892
|
eventCount += 1;
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
type: event.type,
|
|
1939
|
-
properties: event.properties,
|
|
1940
|
-
});
|
|
1941
|
-
if (turnAbortController.signal.aborted || this.activeForegroundTurnId !== turnId) {
|
|
1942
|
-
traceOpenCode("event.skip", {
|
|
1893
|
+
if (!subscriptionReadyResolved) {
|
|
1894
|
+
subscriptionReadyResolved = true;
|
|
1895
|
+
this.traceOpenCode("provider.opencode.subscribe.ready", {
|
|
1943
1896
|
turnId,
|
|
1944
|
-
|
|
1945
|
-
aborted: turnAbortController.signal.aborted,
|
|
1946
|
-
activeTurnId: this.activeForegroundTurnId,
|
|
1897
|
+
sessionId: this.sessionId,
|
|
1947
1898
|
});
|
|
1948
|
-
|
|
1899
|
+
subscriptionReady.resolve();
|
|
1949
1900
|
}
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1901
|
+
const shouldContinue = await this.consumeOpenCodeStreamEvent({
|
|
1902
|
+
rawEvent,
|
|
1903
|
+
eventCount,
|
|
1952
1904
|
turnId,
|
|
1953
|
-
|
|
1954
|
-
count: translated.length,
|
|
1955
|
-
types: translated.map((t) => t.type),
|
|
1905
|
+
turnAbortController,
|
|
1956
1906
|
});
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
traceOpenCode("event.translated.skip-active", { turnId, type: e.type });
|
|
1960
|
-
return;
|
|
1961
|
-
}
|
|
1962
|
-
if (e.type === "timeline" && e.item.type === "tool_call") {
|
|
1963
|
-
this.trackToolCall(e.item);
|
|
1964
|
-
}
|
|
1965
|
-
const terminalEvent = toTerminalTurnEvent(e);
|
|
1966
|
-
if (terminalEvent) {
|
|
1967
|
-
traceOpenCode("event.terminal", { turnId, type: terminalEvent.type });
|
|
1968
|
-
this.finishForegroundTurn(terminalEvent, turnId);
|
|
1969
|
-
return;
|
|
1970
|
-
}
|
|
1971
|
-
this.notifySubscribers(e, turnId);
|
|
1907
|
+
if (!shouldContinue) {
|
|
1908
|
+
return;
|
|
1972
1909
|
}
|
|
1973
1910
|
}
|
|
1974
|
-
traceOpenCode("stream.eof", {
|
|
1911
|
+
this.traceOpenCode("provider.opencode.stream.eof", {
|
|
1975
1912
|
turnId,
|
|
1976
1913
|
eventCount,
|
|
1977
1914
|
aborted: turnAbortController.signal.aborted,
|
|
1978
1915
|
stillActive: this.activeForegroundTurnId === turnId,
|
|
1979
1916
|
});
|
|
1980
1917
|
if (!turnAbortController.signal.aborted && this.activeForegroundTurnId === turnId) {
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
return;
|
|
1918
|
+
this.traceOpenCode("provider.opencode.turn.fail_eof", { turnId, eventCount });
|
|
1919
|
+
if (!subscriptionReadyResolved) {
|
|
1920
|
+
subscriptionReady.reject(new Error("OpenCode event stream ended before it became ready"));
|
|
1985
1921
|
}
|
|
1986
|
-
traceOpenCode("turn.fail.eof", { turnId, eventCount });
|
|
1987
1922
|
this.finishForegroundTurn({
|
|
1988
1923
|
type: "turn_failed",
|
|
1989
1924
|
provider: "opencode",
|
|
@@ -1992,7 +1927,7 @@ class OpenCodeAgentSession {
|
|
|
1992
1927
|
}
|
|
1993
1928
|
}
|
|
1994
1929
|
catch (error) {
|
|
1995
|
-
traceOpenCode("subscribe.error", {
|
|
1930
|
+
this.traceOpenCode("provider.opencode.subscribe.error", {
|
|
1996
1931
|
turnId,
|
|
1997
1932
|
error: error instanceof Error ? { name: error.name, message: error.message } : String(error),
|
|
1998
1933
|
});
|
|
@@ -2018,342 +1953,62 @@ class OpenCodeAgentSession {
|
|
|
2018
1953
|
}
|
|
2019
1954
|
}
|
|
2020
1955
|
}
|
|
2021
|
-
async
|
|
2022
|
-
|
|
1956
|
+
async consumeOpenCodeStreamEvent(params) {
|
|
1957
|
+
const { rawEvent, eventCount, turnId, turnAbortController } = params;
|
|
1958
|
+
const event = unwrapOpenCodeGlobalEvent(rawEvent);
|
|
1959
|
+
this.traceOpenCode("provider.opencode.raw_event", {
|
|
2023
1960
|
turnId,
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
1961
|
+
n: eventCount,
|
|
1962
|
+
type: event?.type,
|
|
1963
|
+
rawType: readOpenCodeRecord(rawEvent)?.type,
|
|
1964
|
+
directory: readOpenCodeRecord(rawEvent)?.directory,
|
|
1965
|
+
rawEvent,
|
|
1966
|
+
properties: event?.properties,
|
|
2029
1967
|
});
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
traceOpenCode("recovery.no-start-time", { turnId });
|
|
2033
|
-
return false;
|
|
2034
|
-
}
|
|
2035
|
-
const completionDeadline = Date.now() + this.recovery.timeoutMs;
|
|
2036
|
-
const livenessDeadline = Date.now() + this.recovery.livenessMs;
|
|
2037
|
-
let attempt = 0;
|
|
2038
|
-
let observedActivity = false;
|
|
2039
|
-
while (true) {
|
|
2040
|
-
if (this.activeForegroundTurnId !== turnId) {
|
|
2041
|
-
traceOpenCode("recovery.cancelled", { turnId, attempt });
|
|
2042
|
-
return true;
|
|
2043
|
-
}
|
|
2044
|
-
attempt += 1;
|
|
2045
|
-
const emittedPromptIds = await this.pollPendingQuestionsAndPermissions(turnId);
|
|
2046
|
-
if (emittedPromptIds > 0) {
|
|
2047
|
-
observedActivity = true;
|
|
2048
|
-
}
|
|
2049
|
-
const outcome = await this.fetchAssistantOutcomeFromMessagesApi(startedAt);
|
|
2050
|
-
traceOpenCode("recovery.poll", {
|
|
2051
|
-
turnId,
|
|
2052
|
-
attempt,
|
|
2053
|
-
kind: outcome?.kind ?? "none",
|
|
2054
|
-
messageId: outcome?.messageId,
|
|
2055
|
-
emittedPromptIds,
|
|
2056
|
-
});
|
|
2057
|
-
if (outcome?.kind === "failure") {
|
|
2058
|
-
this.foregroundKnownMessageIds.add(outcome.messageId);
|
|
2059
|
-
this.finishForegroundTurn({
|
|
2060
|
-
type: "turn_failed",
|
|
2061
|
-
provider: "opencode",
|
|
2062
|
-
error: outcome.error,
|
|
2063
|
-
}, turnId);
|
|
2064
|
-
return true;
|
|
2065
|
-
}
|
|
2066
|
-
if (outcome?.kind === "completion") {
|
|
2067
|
-
this.emitIncrementalAssistantParts(outcome.parts, turnId);
|
|
2068
|
-
return this.applyRecoveredAssistantCompletion(outcome, turnId);
|
|
2069
|
-
}
|
|
2070
|
-
if (outcome?.kind === "in-progress") {
|
|
2071
|
-
observedActivity = true;
|
|
2072
|
-
this.emitIncrementalAssistantParts(outcome.parts, turnId);
|
|
2073
|
-
}
|
|
2074
|
-
const now = Date.now();
|
|
2075
|
-
if (!observedActivity && now >= livenessDeadline) {
|
|
2076
|
-
const deferred = await this.deferForPendingPermissionOrFailRecoveredTurnAfterCap(turnId, attempt, "liveness");
|
|
2077
|
-
if (deferred) {
|
|
2078
|
-
continue;
|
|
2079
|
-
}
|
|
2080
|
-
return true;
|
|
2081
|
-
}
|
|
2082
|
-
if (now >= completionDeadline) {
|
|
2083
|
-
const deferred = await this.deferForPendingPermissionOrFailRecoveredTurnAfterCap(turnId, attempt, "completion");
|
|
2084
|
-
if (deferred) {
|
|
2085
|
-
continue;
|
|
2086
|
-
}
|
|
2087
|
-
return true;
|
|
2088
|
-
}
|
|
2089
|
-
const waitMs = Math.min(this.recovery.pollIntervalMs, completionDeadline - now);
|
|
2090
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
1968
|
+
if (!event) {
|
|
1969
|
+
return true;
|
|
2091
1970
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
if (this.pendingPermissions.size > 0) {
|
|
2095
|
-
// A pending OpenCode question/permission means the turn is blocked on
|
|
2096
|
-
// user input, not dead. Keep polling until the user response lets the
|
|
2097
|
-
// assistant finish or the turn is canceled.
|
|
2098
|
-
traceOpenCode(`recovery.${cap}-deferred-for-permission`, {
|
|
1971
|
+
if (turnAbortController.signal.aborted || this.activeForegroundTurnId !== turnId) {
|
|
1972
|
+
this.traceOpenCode("provider.opencode.event.skip", {
|
|
2099
1973
|
turnId,
|
|
2100
|
-
|
|
2101
|
-
|
|
1974
|
+
n: eventCount,
|
|
1975
|
+
aborted: turnAbortController.signal.aborted,
|
|
1976
|
+
activeTurnId: this.activeForegroundTurnId,
|
|
2102
1977
|
});
|
|
2103
|
-
|
|
2104
|
-
return true;
|
|
1978
|
+
return false;
|
|
2105
1979
|
}
|
|
2106
|
-
|
|
1980
|
+
this.armRetryFailureTimerForStatus(event, turnId);
|
|
1981
|
+
const translated = await this.translateEvent(event);
|
|
1982
|
+
this.traceOpenCode("provider.opencode.parsed_event", {
|
|
2107
1983
|
turnId,
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
}
|
|
2113
|
-
async failRecoveredTurnAfterCap(turnId, cap) {
|
|
2114
|
-
await this.abortOpenCodeSessionAfterRecoveryCap(turnId, cap);
|
|
2115
|
-
this.finishForegroundTurn({
|
|
2116
|
-
type: "turn_failed",
|
|
2117
|
-
provider: "opencode",
|
|
2118
|
-
error: "OpenCode event stream ended before the turn reached a terminal state",
|
|
2119
|
-
}, turnId);
|
|
2120
|
-
}
|
|
2121
|
-
async abortOpenCodeSessionAfterRecoveryCap(turnId, cap) {
|
|
2122
|
-
const abortPromise = this.beginSessionAbort(turnId, `recovery-${cap}`);
|
|
2123
|
-
await withTimeout(abortPromise, OPENCODE_RECOVERY_ABORT_TIMEOUT_MS, "OpenCode session.abort").catch((error) => {
|
|
2124
|
-
this.logger.warn({ err: error, sessionId: this.sessionId, turnId, cap }, "OpenCode session.abort exceeded the EOF recovery cap");
|
|
1984
|
+
n: eventCount,
|
|
1985
|
+
count: translated.length,
|
|
1986
|
+
types: translated.map((t) => t.type),
|
|
1987
|
+
events: translated,
|
|
2125
1988
|
});
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
.then(() => this.client.question.list({ directory: this.config.cwd }))
|
|
2131
|
-
.catch((error) => {
|
|
2132
|
-
traceOpenCode("recovery.question-list.throw", {
|
|
2133
|
-
turnId,
|
|
2134
|
-
error: error instanceof Error
|
|
2135
|
-
? { name: error.name, message: error.message, stack: error.stack }
|
|
2136
|
-
: String(error),
|
|
2137
|
-
});
|
|
2138
|
-
return null;
|
|
2139
|
-
}),
|
|
2140
|
-
Promise.resolve()
|
|
2141
|
-
.then(() => this.client.permission.list({ directory: this.config.cwd }))
|
|
2142
|
-
.catch((error) => {
|
|
2143
|
-
traceOpenCode("recovery.permission-list.throw", {
|
|
2144
|
-
turnId,
|
|
2145
|
-
error: error instanceof Error
|
|
2146
|
-
? { name: error.name, message: error.message, stack: error.stack }
|
|
2147
|
-
: String(error),
|
|
2148
|
-
});
|
|
2149
|
-
return null;
|
|
2150
|
-
}),
|
|
2151
|
-
]);
|
|
2152
|
-
if (this.activeForegroundTurnId !== turnId)
|
|
2153
|
-
return 0;
|
|
2154
|
-
let emitted = 0;
|
|
2155
|
-
for (const question of questionsResponse?.data ?? []) {
|
|
2156
|
-
if (question.sessionID !== this.sessionId)
|
|
2157
|
-
continue;
|
|
2158
|
-
if (this.foregroundEmittedQuestionIds.has(question.id))
|
|
2159
|
-
continue;
|
|
2160
|
-
this.foregroundEmittedQuestionIds.add(question.id);
|
|
2161
|
-
emitted += 1;
|
|
2162
|
-
const synthetic = {
|
|
2163
|
-
id: question.id,
|
|
2164
|
-
type: "question.asked",
|
|
2165
|
-
properties: question,
|
|
2166
|
-
};
|
|
2167
|
-
const events = await this.translateEvent(synthetic);
|
|
2168
|
-
for (const event of events) {
|
|
2169
|
-
this.notifySubscribers(event, turnId);
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
for (const permission of permissionsResponse?.data ?? []) {
|
|
2173
|
-
if (permission.sessionID !== this.sessionId)
|
|
2174
|
-
continue;
|
|
2175
|
-
if (this.foregroundEmittedPermissionIds.has(permission.id))
|
|
2176
|
-
continue;
|
|
2177
|
-
this.foregroundEmittedPermissionIds.add(permission.id);
|
|
2178
|
-
emitted += 1;
|
|
2179
|
-
const synthetic = {
|
|
2180
|
-
id: permission.id,
|
|
2181
|
-
type: "permission.asked",
|
|
2182
|
-
properties: permission,
|
|
2183
|
-
};
|
|
2184
|
-
const events = await this.translateEvent(synthetic);
|
|
2185
|
-
for (const event of events) {
|
|
2186
|
-
this.notifySubscribers(event, turnId);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
return emitted;
|
|
2190
|
-
}
|
|
2191
|
-
async fetchAssistantOutcomeFromMessagesApi(startedAt) {
|
|
2192
|
-
const response = await Promise.resolve()
|
|
2193
|
-
.then(() => this.client.session.messages({
|
|
2194
|
-
sessionID: this.sessionId,
|
|
2195
|
-
directory: this.config.cwd,
|
|
2196
|
-
}))
|
|
2197
|
-
.catch((error) => {
|
|
2198
|
-
traceOpenCode("recovery.messages.throw", {
|
|
2199
|
-
error: error instanceof Error
|
|
2200
|
-
? { name: error.name, message: error.message, stack: error.stack }
|
|
2201
|
-
: String(error),
|
|
2202
|
-
});
|
|
2203
|
-
return null;
|
|
2204
|
-
});
|
|
2205
|
-
if (response === null) {
|
|
2206
|
-
return null;
|
|
2207
|
-
}
|
|
2208
|
-
if (response.error || !response.data) {
|
|
2209
|
-
return null;
|
|
2210
|
-
}
|
|
2211
|
-
for (let index = response.data.length - 1; index >= 0; index -= 1) {
|
|
2212
|
-
const item = response.data[index];
|
|
2213
|
-
if (!item)
|
|
2214
|
-
continue;
|
|
2215
|
-
const info = item.info;
|
|
2216
|
-
if (info.role !== "assistant")
|
|
2217
|
-
continue;
|
|
2218
|
-
if (this.foregroundKnownMessageIds.has(info.id))
|
|
2219
|
-
continue;
|
|
2220
|
-
if (typeof info.time?.created === "number" && info.time.created < startedAt)
|
|
2221
|
-
continue;
|
|
2222
|
-
if (info.error) {
|
|
2223
|
-
return {
|
|
2224
|
-
kind: "failure",
|
|
2225
|
-
messageId: info.id,
|
|
2226
|
-
error: formatOpenCodeAssistantErrorMessage(info.error),
|
|
2227
|
-
};
|
|
1989
|
+
for (const e of translated) {
|
|
1990
|
+
if (this.activeForegroundTurnId !== turnId) {
|
|
1991
|
+
this.traceOpenCode("provider.opencode.parsed_event.skip_active", { turnId, type: e.type });
|
|
1992
|
+
return false;
|
|
2228
1993
|
}
|
|
2229
|
-
if (
|
|
2230
|
-
|
|
1994
|
+
if (e.type === "timeline" && e.item.type === "tool_call") {
|
|
1995
|
+
this.trackToolCall(e.item);
|
|
2231
1996
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
.
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
if (!text)
|
|
2241
|
-
continue;
|
|
2242
|
-
const usage = {};
|
|
2243
|
-
mergeOpenCodeStepFinishUsage(usage, { cost: info.cost, tokens: info.tokens });
|
|
2244
|
-
return { kind: "completion", messageId: info.id, text, parts: item.parts, usage };
|
|
2245
|
-
}
|
|
2246
|
-
return null;
|
|
2247
|
-
}
|
|
2248
|
-
emitIncrementalAssistantParts(parts, turnId) {
|
|
2249
|
-
for (const part of parts) {
|
|
2250
|
-
if (part.type === "reasoning" && part.text) {
|
|
2251
|
-
const emittedTextLength = this.foregroundEmittedReasoningTextLengthByPartId.get(part.id) ?? 0;
|
|
2252
|
-
if (part.text.length <= emittedTextLength)
|
|
2253
|
-
continue;
|
|
2254
|
-
const text = part.text.slice(emittedTextLength);
|
|
2255
|
-
this.foregroundEmittedReasoningTextLengthByPartId.set(part.id, part.text.length);
|
|
2256
|
-
this.notifySubscribers({
|
|
2257
|
-
type: "timeline",
|
|
2258
|
-
provider: "opencode",
|
|
2259
|
-
item: { type: "reasoning", text },
|
|
2260
|
-
}, turnId);
|
|
2261
|
-
continue;
|
|
1997
|
+
const terminalEvent = toTerminalTurnEvent(e);
|
|
1998
|
+
if (terminalEvent) {
|
|
1999
|
+
this.traceOpenCode("provider.opencode.event.terminal", {
|
|
2000
|
+
turnId,
|
|
2001
|
+
type: terminalEvent.type,
|
|
2002
|
+
});
|
|
2003
|
+
this.finishForegroundTurn(terminalEvent, turnId);
|
|
2004
|
+
return false;
|
|
2262
2005
|
}
|
|
2263
|
-
|
|
2264
|
-
continue;
|
|
2265
|
-
const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
|
|
2266
|
-
if (!parsedToolPart.success || !parsedToolPart.data)
|
|
2267
|
-
continue;
|
|
2268
|
-
const callId = parsedToolPart.data.callId;
|
|
2269
|
-
const signature = this.createRecoveredToolCallSignature(part, parsedToolPart.data);
|
|
2270
|
-
const lastSignature = this.foregroundEmittedToolCallSignatureByCallId.get(callId);
|
|
2271
|
-
if (lastSignature === signature)
|
|
2272
|
-
continue;
|
|
2273
|
-
this.foregroundEmittedToolCallSignatureByCallId.set(callId, signature);
|
|
2274
|
-
this.trackToolCall(parsedToolPart.data);
|
|
2275
|
-
this.notifySubscribers({
|
|
2276
|
-
type: "timeline",
|
|
2277
|
-
provider: "opencode",
|
|
2278
|
-
item: parsedToolPart.data,
|
|
2279
|
-
}, turnId);
|
|
2280
|
-
}
|
|
2281
|
-
}
|
|
2282
|
-
createRecoveredToolCallSignature(part, item) {
|
|
2283
|
-
const state = part
|
|
2284
|
-
.state;
|
|
2285
|
-
return JSON.stringify([
|
|
2286
|
-
item.callId,
|
|
2287
|
-
item.status,
|
|
2288
|
-
state?.input ?? null,
|
|
2289
|
-
state?.output ?? null,
|
|
2290
|
-
state?.error ?? null,
|
|
2291
|
-
]);
|
|
2292
|
-
}
|
|
2293
|
-
applyRecoveredAssistantCompletion(completion, turnId) {
|
|
2294
|
-
if (this.activeForegroundTurnId !== turnId) {
|
|
2295
|
-
return false;
|
|
2296
|
-
}
|
|
2297
|
-
this.foregroundKnownMessageIds.add(completion.messageId);
|
|
2298
|
-
this.logger.warn({ sessionId: this.sessionId, turnId }, "Recovered OpenCode turn completion via messages API after SSE EOF");
|
|
2299
|
-
const recoveryText = this.resolvePersistedAssistantRecoveryText(completion.text);
|
|
2300
|
-
if (recoveryText === null) {
|
|
2301
|
-
return false;
|
|
2006
|
+
this.notifySubscribers(e, turnId);
|
|
2302
2007
|
}
|
|
2303
|
-
if (recoveryText.length > 0) {
|
|
2304
|
-
this.notifySubscribers({
|
|
2305
|
-
type: "timeline",
|
|
2306
|
-
provider: "opencode",
|
|
2307
|
-
item: { type: "assistant_message", text: recoveryText },
|
|
2308
|
-
}, turnId);
|
|
2309
|
-
this.foregroundAssistantMessageEmitted = true;
|
|
2310
|
-
}
|
|
2311
|
-
if (hasNormalizedOpenCodeUsage(completion.usage) && !this.foregroundUsageUpdated) {
|
|
2312
|
-
this.accumulatedUsage = {
|
|
2313
|
-
...this.accumulatedUsage,
|
|
2314
|
-
...completion.usage,
|
|
2315
|
-
};
|
|
2316
|
-
this.notifySubscribers({
|
|
2317
|
-
type: "usage_updated",
|
|
2318
|
-
provider: "opencode",
|
|
2319
|
-
usage: { ...this.accumulatedUsage },
|
|
2320
|
-
}, turnId);
|
|
2321
|
-
this.foregroundUsageUpdated = true;
|
|
2322
|
-
}
|
|
2323
|
-
this.finishForegroundTurn({
|
|
2324
|
-
type: "turn_completed",
|
|
2325
|
-
provider: "opencode",
|
|
2326
|
-
usage: hasNormalizedOpenCodeUsage(this.accumulatedUsage)
|
|
2327
|
-
? { ...this.accumulatedUsage }
|
|
2328
|
-
: undefined,
|
|
2329
|
-
}, turnId);
|
|
2330
2008
|
return true;
|
|
2331
2009
|
}
|
|
2332
|
-
resolvePersistedAssistantRecoveryText(completedText) {
|
|
2333
|
-
if (!this.foregroundAssistantMessageEmitted) {
|
|
2334
|
-
return completedText;
|
|
2335
|
-
}
|
|
2336
|
-
if (completedText === this.foregroundAssistantText) {
|
|
2337
|
-
return "";
|
|
2338
|
-
}
|
|
2339
|
-
return completedText.startsWith(this.foregroundAssistantText)
|
|
2340
|
-
? completedText.slice(this.foregroundAssistantText.length)
|
|
2341
|
-
: null;
|
|
2342
|
-
}
|
|
2343
|
-
async readPersistedSessionMessageIds() {
|
|
2344
|
-
const messageRoot = path.join(this.storageRoot, "message", this.sessionId);
|
|
2345
|
-
const messageFiles = await findJsonFiles(messageRoot);
|
|
2346
|
-
const messageIds = new Set();
|
|
2347
|
-
for (const file of messageFiles) {
|
|
2348
|
-
const parsed = await readJsonFile(file, OpenCodeStoredMessageSchema);
|
|
2349
|
-
if (parsed?.sessionID === this.sessionId) {
|
|
2350
|
-
messageIds.add(parsed.id);
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
return messageIds;
|
|
2354
|
-
}
|
|
2355
2010
|
finishForegroundTurn(event, turnId) {
|
|
2356
|
-
traceOpenCode("
|
|
2011
|
+
this.traceOpenCode("provider.opencode.finish_foreground_turn", {
|
|
2357
2012
|
turnId,
|
|
2358
2013
|
activeTurnId: this.activeForegroundTurnId,
|
|
2359
2014
|
type: event.type,
|
|
@@ -2369,7 +2024,7 @@ class OpenCodeAgentSession {
|
|
|
2369
2024
|
else {
|
|
2370
2025
|
this.runningToolCalls.clear();
|
|
2371
2026
|
}
|
|
2372
|
-
this.
|
|
2027
|
+
this.clearRetryFailureTimer();
|
|
2373
2028
|
this.activeForegroundTurnId = null;
|
|
2374
2029
|
// Abort the SSE connection so the SDK tears down the underlying fetch.
|
|
2375
2030
|
this.abortController?.abort();
|
|
@@ -2383,6 +2038,37 @@ class OpenCodeAgentSession {
|
|
|
2383
2038
|
}
|
|
2384
2039
|
this.runningToolCalls.delete(item.callId);
|
|
2385
2040
|
}
|
|
2041
|
+
armRetryFailureTimerForStatus(event, turnId) {
|
|
2042
|
+
if (this.retryFailureTimer || event.type !== "session.status") {
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
if (event.properties.sessionID !== this.sessionId || event.properties.status.type !== "retry") {
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
const retry = event.properties.status;
|
|
2049
|
+
const message = typeof retry.message === "string" ? retry.message.trim() : "";
|
|
2050
|
+
const error = message
|
|
2051
|
+
? `OpenCode provider retry did not recover: ${message}`
|
|
2052
|
+
: "OpenCode provider retry did not recover";
|
|
2053
|
+
this.retryFailureTimer = setTimeout(() => {
|
|
2054
|
+
this.retryFailureTimer = null;
|
|
2055
|
+
if (this.activeForegroundTurnId !== turnId) {
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
this.finishForegroundTurn({
|
|
2059
|
+
type: "turn_failed",
|
|
2060
|
+
provider: "opencode",
|
|
2061
|
+
error,
|
|
2062
|
+
}, turnId);
|
|
2063
|
+
}, OPENCODE_RETRY_STATUS_FAILURE_MS);
|
|
2064
|
+
}
|
|
2065
|
+
clearRetryFailureTimer() {
|
|
2066
|
+
if (!this.retryFailureTimer) {
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
clearTimeout(this.retryFailureTimer);
|
|
2070
|
+
this.retryFailureTimer = null;
|
|
2071
|
+
}
|
|
2386
2072
|
synthesizeInterruptedToolCalls(turnId) {
|
|
2387
2073
|
for (const item of this.runningToolCalls.values()) {
|
|
2388
2074
|
const error = { message: "Tool execution aborted" };
|
|
@@ -2408,14 +2094,11 @@ class OpenCodeAgentSession {
|
|
|
2408
2094
|
}
|
|
2409
2095
|
notifySubscribers(event, turnIdOverride) {
|
|
2410
2096
|
const turnId = turnIdOverride ?? this.activeForegroundTurnId;
|
|
2411
|
-
if (event.type === "timeline" && event.item.type === "assistant_message") {
|
|
2412
|
-
this.foregroundAssistantMessageEmitted = true;
|
|
2413
|
-
this.foregroundAssistantText += event.item.text;
|
|
2414
|
-
}
|
|
2415
|
-
if (event.type === "usage_updated") {
|
|
2416
|
-
this.foregroundUsageUpdated = true;
|
|
2417
|
-
}
|
|
2418
2097
|
const tagged = turnId ? { ...event, turnId } : event;
|
|
2098
|
+
this.traceOpenCode("provider.opencode.event_emit", {
|
|
2099
|
+
turnId: getAgentStreamEventTurnId(tagged),
|
|
2100
|
+
event: tagged,
|
|
2101
|
+
});
|
|
2419
2102
|
for (const callback of this.subscribers) {
|
|
2420
2103
|
try {
|
|
2421
2104
|
callback(tagged);
|
|
@@ -2428,6 +2111,15 @@ class OpenCodeAgentSession {
|
|
|
2428
2111
|
createTurnId() {
|
|
2429
2112
|
return `opencode-turn-${this.nextTurnOrdinal++}`;
|
|
2430
2113
|
}
|
|
2114
|
+
traceOpenCode(msg, data = {}) {
|
|
2115
|
+
this.logger.trace({
|
|
2116
|
+
agentId: this.agentId,
|
|
2117
|
+
provider: "opencode",
|
|
2118
|
+
sessionId: this.sessionId,
|
|
2119
|
+
turnId: data.turnId ?? this.activeForegroundTurnId ?? undefined,
|
|
2120
|
+
...data,
|
|
2121
|
+
}, msg);
|
|
2122
|
+
}
|
|
2431
2123
|
async *streamHistory() {
|
|
2432
2124
|
const response = await this.client.session.messages({
|
|
2433
2125
|
sessionID: this.sessionId,
|