@getpaseo/server 0.1.76 → 0.1.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/client/daemon-client.d.ts +1 -0
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +1 -0
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +1 -1
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +8 -2
- package/dist/server/server/agent/agent-manager.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/import-sessions.d.ts.map +1 -1
- package/dist/server/server/agent/import-sessions.js +6 -2
- package/dist/server/server/agent/import-sessions.js.map +1 -1
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +153 -1
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- 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 +3 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode/server-manager.js +2 -1
- package/dist/server/server/agent/providers/opencode/server-manager.js.map +1 -1
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +7 -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 +50 -1
- 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 -4
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +249 -286
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts +1 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +8 -2
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/config.d.ts.map +1 -1
- package/dist/server/server/config.js +10 -4
- package/dist/server/server/config.js.map +1 -1
- package/dist/server/server/pairing-offer.d.ts +1 -0
- package/dist/server/server/pairing-offer.d.ts.map +1 -1
- package/dist/server/server/pairing-offer.js +2 -1
- package/dist/server/server/pairing-offer.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +9 -0
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- package/dist/server/server/persisted-config.js +10 -3
- package/dist/server/server/persisted-config.js.map +1 -1
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +2 -1
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -1
- package/dist/server/server/utils/diff-highlighter.js +30 -9
- package/dist/server/server/utils/diff-highlighter.js.map +1 -1
- package/dist/server/shared/messages.d.ts +16 -0
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +1 -0
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/utils/directory-suggestions.d.ts +2 -0
- package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
- package/dist/server/utils/directory-suggestions.js +39 -3
- package/dist/server/utils/directory-suggestions.js.map +1 -1
- package/dist/server/utils/path.d.ts +10 -0
- package/dist/server/utils/path.d.ts.map +1 -1
- package/dist/server/utils/path.js +65 -1
- package/dist/server/utils/path.js.map +1 -1
- package/dist/src/server/persisted-config.js +10 -3
- package/dist/src/server/persisted-config.js.map +1 -1
- package/dist/src/shared/messages.js +1 -0
- package/dist/src/shared/messages.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
2
1
|
import { homedir } from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
2
|
import { findExecutable, isCommandAvailable } from "../../../utils/executable.js";
|
|
3
|
+
import { createPathEquivalenceMatcher } from "../../../utils/path.js";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
|
|
7
6
|
import { createProviderEnvSpec } from "../provider-launch-config.js";
|
|
@@ -25,7 +24,7 @@ const OPENCODE_CAPABILITIES = {
|
|
|
25
24
|
};
|
|
26
25
|
const OPENCODE_BUILD_MODE_ID = "build";
|
|
27
26
|
const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
|
|
28
|
-
const
|
|
27
|
+
const OPENCODE_PERSISTED_SESSION_LIMIT = 200;
|
|
29
28
|
const OPENCODE_PENDING_ABORT_START_TIMEOUT_MS = 10000;
|
|
30
29
|
const DEFAULT_MODES = [
|
|
31
30
|
{
|
|
@@ -44,44 +43,6 @@ const DEFAULT_MODES = [
|
|
|
44
43
|
description: "Automatically approves all tool permission prompts for the session",
|
|
45
44
|
},
|
|
46
45
|
];
|
|
47
|
-
const OpenCodeStoredSessionSchema = z
|
|
48
|
-
.object({
|
|
49
|
-
id: z.string().min(1),
|
|
50
|
-
directory: z.string().min(1),
|
|
51
|
-
title: z.string().nullable().optional(),
|
|
52
|
-
time: z
|
|
53
|
-
.object({
|
|
54
|
-
created: z.number().optional(),
|
|
55
|
-
updated: z.number().optional(),
|
|
56
|
-
})
|
|
57
|
-
.optional(),
|
|
58
|
-
})
|
|
59
|
-
.passthrough();
|
|
60
|
-
const OpenCodeStoredMessageSchema = z
|
|
61
|
-
.object({
|
|
62
|
-
id: z.string().min(1),
|
|
63
|
-
sessionID: z.string().min(1),
|
|
64
|
-
role: z.string().optional(),
|
|
65
|
-
time: z
|
|
66
|
-
.object({
|
|
67
|
-
created: z.number().optional(),
|
|
68
|
-
completed: z.number().optional(),
|
|
69
|
-
})
|
|
70
|
-
.optional(),
|
|
71
|
-
})
|
|
72
|
-
.passthrough();
|
|
73
|
-
const OpenCodeStoredPartSchema = z
|
|
74
|
-
.object({
|
|
75
|
-
type: z.string().optional(),
|
|
76
|
-
text: z.string().optional(),
|
|
77
|
-
time: z
|
|
78
|
-
.object({
|
|
79
|
-
start: z.number().optional(),
|
|
80
|
-
end: z.number().optional(),
|
|
81
|
-
})
|
|
82
|
-
.optional(),
|
|
83
|
-
})
|
|
84
|
-
.passthrough();
|
|
85
46
|
const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
|
|
86
47
|
const OPENCODE_PROVIDER_LIST_TIMEOUT_MS = 30000;
|
|
87
48
|
const OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS = [
|
|
@@ -505,35 +466,30 @@ function buildOpenCodePromptParts(prompt) {
|
|
|
505
466
|
}
|
|
506
467
|
return output;
|
|
507
468
|
}
|
|
508
|
-
function
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
:
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
469
|
+
async function collectOpenCodePersistedAgentsFromSdk(client, options) {
|
|
470
|
+
const limit = options?.limit ?? OPENCODE_PERSISTED_SESSION_LIMIT;
|
|
471
|
+
const sessionListLimit = options?.cwd ? Math.max(limit, OPENCODE_PERSISTED_SESSION_LIMIT) : limit;
|
|
472
|
+
const response = await client.experimental.session.list({
|
|
473
|
+
archived: true,
|
|
474
|
+
roots: true,
|
|
475
|
+
limit: sessionListLimit,
|
|
476
|
+
});
|
|
477
|
+
if (response.error) {
|
|
478
|
+
throw new Error(`Failed to list OpenCode sessions: ${JSON.stringify(response.error)}`);
|
|
479
|
+
}
|
|
480
|
+
const sessions = response.data ?? [];
|
|
481
|
+
const matchesCwd = options?.cwd ? createPathEquivalenceMatcher(options.cwd) : null;
|
|
518
482
|
const candidates = sessions
|
|
519
|
-
.filter((session) => !
|
|
483
|
+
.filter((session) => !matchesCwd || matchesCwd(session.directory))
|
|
520
484
|
.sort((left, right) => getOpenCodeSessionTimestamp(right) - getOpenCodeSessionTimestamp(left))
|
|
521
485
|
.slice(0, limit);
|
|
522
|
-
return await Promise.all(candidates.map((session) => buildOpenCodePersistedAgentDescriptor(
|
|
486
|
+
return await Promise.all(candidates.map((session) => buildOpenCodePersistedAgentDescriptor(client, session)));
|
|
523
487
|
}
|
|
524
|
-
async function
|
|
525
|
-
const
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (parsed) {
|
|
530
|
-
sessions.push(parsed);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
return sessions;
|
|
534
|
-
}
|
|
535
|
-
async function buildOpenCodePersistedAgentDescriptor(storageRoot, session) {
|
|
536
|
-
const timeline = await readOpenCodeSessionTimeline(storageRoot, session.id);
|
|
488
|
+
async function buildOpenCodePersistedAgentDescriptor(client, session) {
|
|
489
|
+
const messages = await readOpenCodeSessionMessagesFromSdk(client, session);
|
|
490
|
+
const timeline = buildOpenCodeSessionTimeline(messages);
|
|
491
|
+
const modeId = resolveOpenCodePersistedSessionModeId(session, messages);
|
|
492
|
+
const model = resolveOpenCodePersistedSessionModel(session, messages);
|
|
537
493
|
return {
|
|
538
494
|
provider: "opencode",
|
|
539
495
|
sessionId: session.id,
|
|
@@ -548,84 +504,13 @@ async function buildOpenCodePersistedAgentDescriptor(storageRoot, session) {
|
|
|
548
504
|
provider: "opencode",
|
|
549
505
|
cwd: session.directory,
|
|
550
506
|
title: normalizeOpenCodeSessionTitle(session.title),
|
|
507
|
+
...(modeId ? { modeId } : {}),
|
|
508
|
+
...(model ? { model } : {}),
|
|
551
509
|
},
|
|
552
510
|
},
|
|
553
511
|
timeline,
|
|
554
512
|
};
|
|
555
513
|
}
|
|
556
|
-
async function readOpenCodeSessionTimeline(storageRoot, sessionId) {
|
|
557
|
-
const messageRoot = path.join(storageRoot, "message", sessionId);
|
|
558
|
-
const messageFiles = await findJsonFiles(messageRoot);
|
|
559
|
-
const messages = [];
|
|
560
|
-
for (const file of messageFiles) {
|
|
561
|
-
const parsed = await readJsonFile(file, OpenCodeStoredMessageSchema);
|
|
562
|
-
if (parsed?.sessionID === sessionId) {
|
|
563
|
-
messages.push(parsed);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
const timeline = [];
|
|
567
|
-
for (const message of messages.sort((left, right) => getOpenCodeMessageTimestamp(left) - getOpenCodeMessageTimestamp(right))) {
|
|
568
|
-
const text = await readOpenCodeMessageText(storageRoot, message.id);
|
|
569
|
-
if (!text) {
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
if (message.role === "user") {
|
|
573
|
-
timeline.push({ type: "user_message", text, messageId: message.id });
|
|
574
|
-
}
|
|
575
|
-
else if (message.role === "assistant") {
|
|
576
|
-
timeline.push({ type: "assistant_message", text });
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return timeline;
|
|
580
|
-
}
|
|
581
|
-
async function readOpenCodeMessageText(storageRoot, messageId) {
|
|
582
|
-
const parts = await readOpenCodeStoredParts(storageRoot, messageId);
|
|
583
|
-
return readOpenCodeTextFromParts(parts);
|
|
584
|
-
}
|
|
585
|
-
async function readOpenCodeStoredParts(storageRoot, messageId) {
|
|
586
|
-
const partRoot = path.join(storageRoot, "part", messageId);
|
|
587
|
-
const partFiles = await findJsonFiles(partRoot);
|
|
588
|
-
const parts = [];
|
|
589
|
-
for (const file of partFiles) {
|
|
590
|
-
const parsed = await readJsonFile(file, OpenCodeStoredPartSchema);
|
|
591
|
-
if (parsed) {
|
|
592
|
-
parts.push(parsed);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return parts.sort((left, right) => getOpenCodePartTimestamp(left) - getOpenCodePartTimestamp(right));
|
|
596
|
-
}
|
|
597
|
-
function readOpenCodeTextFromParts(parts) {
|
|
598
|
-
return parts
|
|
599
|
-
.filter((part) => part.type === "text" && typeof part.text === "string")
|
|
600
|
-
.map((part) => part.text?.trim() ?? "")
|
|
601
|
-
.filter(Boolean)
|
|
602
|
-
.join("\n\n");
|
|
603
|
-
}
|
|
604
|
-
async function findJsonFiles(root) {
|
|
605
|
-
let entries;
|
|
606
|
-
try {
|
|
607
|
-
entries = await readdir(root, { withFileTypes: true });
|
|
608
|
-
}
|
|
609
|
-
catch {
|
|
610
|
-
return [];
|
|
611
|
-
}
|
|
612
|
-
const files = await Promise.all(entries.map(async (entry) => {
|
|
613
|
-
const entryPath = path.join(root, entry.name);
|
|
614
|
-
if (entry.isDirectory()) {
|
|
615
|
-
return findJsonFiles(entryPath);
|
|
616
|
-
}
|
|
617
|
-
return entry.isFile() && entry.name.endsWith(".json") ? [entryPath] : [];
|
|
618
|
-
}));
|
|
619
|
-
return files.flat();
|
|
620
|
-
}
|
|
621
|
-
async function readJsonFile(file, schema) {
|
|
622
|
-
try {
|
|
623
|
-
return schema.parse(JSON.parse(await readFile(file, "utf8")));
|
|
624
|
-
}
|
|
625
|
-
catch {
|
|
626
|
-
return null;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
514
|
function normalizeOpenCodeSessionTitle(title) {
|
|
630
515
|
const normalized = title?.trim();
|
|
631
516
|
return normalized ? normalized : null;
|
|
@@ -633,12 +518,6 @@ function normalizeOpenCodeSessionTitle(title) {
|
|
|
633
518
|
function getOpenCodeSessionTimestamp(session) {
|
|
634
519
|
return session.time?.updated ?? session.time?.created ?? 0;
|
|
635
520
|
}
|
|
636
|
-
function getOpenCodeMessageTimestamp(message) {
|
|
637
|
-
return message.time?.created ?? message.time?.completed ?? 0;
|
|
638
|
-
}
|
|
639
|
-
function getOpenCodePartTimestamp(part) {
|
|
640
|
-
return part.time?.start ?? part.time?.end ?? 0;
|
|
641
|
-
}
|
|
642
521
|
function resolveOpenCodeReplayTimestamp(params) {
|
|
643
522
|
const timedPart = params.part;
|
|
644
523
|
const partTimestamp = timedPart?.time?.start ??
|
|
@@ -688,6 +567,82 @@ function buildOpenCodeReplayPartTimelineEvent(params) {
|
|
|
688
567
|
part,
|
|
689
568
|
});
|
|
690
569
|
}
|
|
570
|
+
async function readOpenCodeSessionMessagesFromSdk(client, session) {
|
|
571
|
+
const response = await client.session.messages({
|
|
572
|
+
sessionID: session.id,
|
|
573
|
+
directory: session.directory,
|
|
574
|
+
});
|
|
575
|
+
if (response.error || !response.data) {
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
return response.data;
|
|
579
|
+
}
|
|
580
|
+
function buildOpenCodeSessionTimeline(messages) {
|
|
581
|
+
return messages.flatMap((message) => buildOpenCodeReplayTimelineEvents(message).map((event) => event.item));
|
|
582
|
+
}
|
|
583
|
+
function resolveOpenCodePersistedSessionModeId(session, messages) {
|
|
584
|
+
const agent = session.agent ?? messages.map(readOpenCodeMessageAgent).find(Boolean);
|
|
585
|
+
return agent ? normalizeOpenCodeModeId(agent) : undefined;
|
|
586
|
+
}
|
|
587
|
+
function readOpenCodeMessageAgent(message) {
|
|
588
|
+
const agent = message.info.agent;
|
|
589
|
+
return typeof agent === "string" && agent.trim() ? agent : undefined;
|
|
590
|
+
}
|
|
591
|
+
function resolveOpenCodePersistedSessionModel(session, messages) {
|
|
592
|
+
if (session.model) {
|
|
593
|
+
return buildOpenCodeModelLookupKey(session.model.providerID, session.model.id);
|
|
594
|
+
}
|
|
595
|
+
const model = messages.map(readOpenCodeMessageModel).find(Boolean);
|
|
596
|
+
return model ? buildOpenCodeModelLookupKey(model.providerID, model.modelID) : undefined;
|
|
597
|
+
}
|
|
598
|
+
function readOpenCodeMessageModel(message) {
|
|
599
|
+
const { info } = message;
|
|
600
|
+
if (info.role === "user") {
|
|
601
|
+
return info.model;
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
providerID: info.providerID,
|
|
605
|
+
modelID: info.modelID,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function buildOpenCodeReplayTimelineEvents(message) {
|
|
609
|
+
const { info, parts } = message;
|
|
610
|
+
if (info.role === "user") {
|
|
611
|
+
const text = parts
|
|
612
|
+
.filter((part) => part.type === "text")
|
|
613
|
+
.map((part) => part.text)
|
|
614
|
+
.join("");
|
|
615
|
+
return text
|
|
616
|
+
? [
|
|
617
|
+
buildOpenCodeReplayTimelineEvent({
|
|
618
|
+
item: { type: "user_message", text, messageId: info.id },
|
|
619
|
+
message: info,
|
|
620
|
+
}),
|
|
621
|
+
]
|
|
622
|
+
: [];
|
|
623
|
+
}
|
|
624
|
+
const events = [];
|
|
625
|
+
let emittedAssistantText = false;
|
|
626
|
+
for (const part of parts) {
|
|
627
|
+
if (part.type === "text" && part.text) {
|
|
628
|
+
emittedAssistantText = true;
|
|
629
|
+
}
|
|
630
|
+
const event = buildOpenCodeReplayPartTimelineEvent({ part, message: info });
|
|
631
|
+
if (event) {
|
|
632
|
+
events.push(event);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
if (!emittedAssistantText) {
|
|
636
|
+
const text = stringifyStructuredAssistantMessage(info.structured);
|
|
637
|
+
if (text) {
|
|
638
|
+
events.push(buildOpenCodeReplayTimelineEvent({
|
|
639
|
+
item: { type: "assistant_message", text },
|
|
640
|
+
message: info,
|
|
641
|
+
}));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return events;
|
|
645
|
+
}
|
|
691
646
|
export const __openCodeInternals = {
|
|
692
647
|
buildOpenCodePromptParts,
|
|
693
648
|
buildOpenCodeModelContextWindowLookup,
|
|
@@ -723,13 +678,12 @@ class ProductionOpenCodeRuntime {
|
|
|
723
678
|
}
|
|
724
679
|
}
|
|
725
680
|
export class OpenCodeAgentClient {
|
|
726
|
-
constructor(logger, runtimeSettings,
|
|
681
|
+
constructor(logger, runtimeSettings, deps = {}) {
|
|
727
682
|
this.provider = "opencode";
|
|
728
683
|
this.capabilities = OPENCODE_CAPABILITIES;
|
|
729
684
|
this.modelContextWindows = new Map();
|
|
730
685
|
this.logger = logger.child({ module: "agent", provider: "opencode" });
|
|
731
686
|
this.runtimeSettings = runtimeSettings;
|
|
732
|
-
this.storageRoot = storageRoot ?? resolveOpenCodeStorageRoot();
|
|
733
687
|
this.runtime =
|
|
734
688
|
deps.runtime ??
|
|
735
689
|
new ProductionOpenCodeRuntime(OpenCodeServerManager.getInstance(this.logger, runtimeSettings));
|
|
@@ -752,7 +706,7 @@ export class OpenCodeAgentClient {
|
|
|
752
706
|
throw new Error("OpenCode session creation returned no data");
|
|
753
707
|
}
|
|
754
708
|
await this.populateModelContextWindowCache(client, openCodeConfig.cwd);
|
|
755
|
-
return new OpenCodeAgentSession(openCodeConfig, client, session.id, this.logger,
|
|
709
|
+
return new OpenCodeAgentSession(openCodeConfig, client, session.id, this.logger, new Map(this.modelContextWindows), acquisition.release, options?.persistSession, launchContext?.agentId);
|
|
756
710
|
}
|
|
757
711
|
catch (error) {
|
|
758
712
|
acquisition.release();
|
|
@@ -760,14 +714,16 @@ export class OpenCodeAgentClient {
|
|
|
760
714
|
}
|
|
761
715
|
}
|
|
762
716
|
async resumeSession(handle, overrides, launchContext) {
|
|
763
|
-
const
|
|
717
|
+
const metadata = (handle.metadata ?? {});
|
|
718
|
+
const cwd = overrides?.cwd ?? metadata.cwd;
|
|
764
719
|
if (!cwd) {
|
|
765
720
|
throw new Error("OpenCode resume requires the original working directory");
|
|
766
721
|
}
|
|
767
722
|
const config = {
|
|
723
|
+
...metadata,
|
|
724
|
+
...overrides,
|
|
768
725
|
provider: "opencode",
|
|
769
726
|
cwd,
|
|
770
|
-
...overrides,
|
|
771
727
|
};
|
|
772
728
|
const openCodeConfig = this.assertConfig(config);
|
|
773
729
|
const acquisition = await this.runtime.acquireServer({ force: false });
|
|
@@ -778,7 +734,7 @@ export class OpenCodeAgentClient {
|
|
|
778
734
|
});
|
|
779
735
|
try {
|
|
780
736
|
await this.populateModelContextWindowCache(client, openCodeConfig.cwd);
|
|
781
|
-
return new OpenCodeAgentSession(openCodeConfig, client, handle.sessionId, this.logger,
|
|
737
|
+
return new OpenCodeAgentSession(openCodeConfig, client, handle.sessionId, this.logger, new Map(this.modelContextWindows), acquisition.release, undefined, launchContext?.agentId);
|
|
782
738
|
}
|
|
783
739
|
catch (error) {
|
|
784
740
|
acquisition.release();
|
|
@@ -858,8 +814,37 @@ export class OpenCodeAgentClient {
|
|
|
858
814
|
acquisition.release();
|
|
859
815
|
}
|
|
860
816
|
}
|
|
817
|
+
async listCommands(config) {
|
|
818
|
+
const openCodeConfig = this.assertConfig(config);
|
|
819
|
+
const acquisition = await this.runtime.acquireServer({ force: false });
|
|
820
|
+
const { url } = acquisition.server;
|
|
821
|
+
const client = this.runtime.createClient({
|
|
822
|
+
baseUrl: url,
|
|
823
|
+
directory: openCodeConfig.cwd,
|
|
824
|
+
});
|
|
825
|
+
try {
|
|
826
|
+
return await listOpenCodeCommandsFromSdk(client, openCodeConfig.cwd);
|
|
827
|
+
}
|
|
828
|
+
finally {
|
|
829
|
+
acquisition.release();
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async listFeatures(_config) {
|
|
833
|
+
return [];
|
|
834
|
+
}
|
|
861
835
|
async listPersistedAgents(options) {
|
|
862
|
-
|
|
836
|
+
const acquisition = await this.runtime.acquireServer({ force: false });
|
|
837
|
+
const { url } = acquisition.server;
|
|
838
|
+
const client = this.runtime.createClient({
|
|
839
|
+
baseUrl: url,
|
|
840
|
+
directory: options?.cwd ?? "",
|
|
841
|
+
});
|
|
842
|
+
try {
|
|
843
|
+
return await collectOpenCodePersistedAgentsFromSdk(client, options);
|
|
844
|
+
}
|
|
845
|
+
finally {
|
|
846
|
+
acquisition.release();
|
|
847
|
+
}
|
|
863
848
|
}
|
|
864
849
|
async isAvailable() {
|
|
865
850
|
const command = this.runtimeSettings?.command;
|
|
@@ -978,6 +963,21 @@ function stringifyStructuredAssistantMessage(value) {
|
|
|
978
963
|
return null;
|
|
979
964
|
}
|
|
980
965
|
}
|
|
966
|
+
async function listOpenCodeCommandsFromSdk(client, directory) {
|
|
967
|
+
const result = await client.command.list({ directory });
|
|
968
|
+
const commandsByName = new Map(OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS.map((command) => [command.name, command]));
|
|
969
|
+
if (result.error || !result.data) {
|
|
970
|
+
return Array.from(commandsByName.values());
|
|
971
|
+
}
|
|
972
|
+
for (const cmd of result.data) {
|
|
973
|
+
commandsByName.set(cmd.name, {
|
|
974
|
+
name: cmd.name,
|
|
975
|
+
description: cmd.description ?? "",
|
|
976
|
+
argumentHint: cmd.hints?.length ? cmd.hints.join(" ") : "",
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
return Array.from(commandsByName.values());
|
|
980
|
+
}
|
|
981
981
|
function readOpenCodeRecord(value) {
|
|
982
982
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
983
983
|
? value
|
|
@@ -1634,7 +1634,7 @@ function unwrapOpenCodeGlobalEvent(event) {
|
|
|
1634
1634
|
return null;
|
|
1635
1635
|
}
|
|
1636
1636
|
class OpenCodeAgentSession {
|
|
1637
|
-
constructor(config, client, sessionId, logger,
|
|
1637
|
+
constructor(config, client, sessionId, logger, modelContextWindowsByModelKey = new Map(), releaseServer, persistSession = true, agentId) {
|
|
1638
1638
|
this.agentId = agentId;
|
|
1639
1639
|
this.provider = "opencode";
|
|
1640
1640
|
this.capabilities = OPENCODE_CAPABILITIES;
|
|
@@ -1661,6 +1661,9 @@ class OpenCodeAgentSession {
|
|
|
1661
1661
|
this.subAgentsByCallId = new Map();
|
|
1662
1662
|
this.subAgentCallIdByChildSessionId = new Map();
|
|
1663
1663
|
this.pendingChildToolPartsBySessionId = new Map();
|
|
1664
|
+
this.eventStreamAbortController = null;
|
|
1665
|
+
this.eventStreamReady = null;
|
|
1666
|
+
this.closed = false;
|
|
1664
1667
|
this.deletedFromProvider = false;
|
|
1665
1668
|
this.config = config;
|
|
1666
1669
|
this.client = client;
|
|
@@ -1671,6 +1674,7 @@ class OpenCodeAgentSession {
|
|
|
1671
1674
|
this.releaseServer = releaseServer ?? null;
|
|
1672
1675
|
this.persistSession = persistSession;
|
|
1673
1676
|
this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(config.model);
|
|
1677
|
+
this.startEventStream();
|
|
1674
1678
|
}
|
|
1675
1679
|
get id() {
|
|
1676
1680
|
return this.sessionId;
|
|
@@ -1767,22 +1771,17 @@ class OpenCodeAgentSession {
|
|
|
1767
1771
|
const thinkingOptionId = this.config.thinkingOptionId;
|
|
1768
1772
|
const effectiveVariant = thinkingOptionId ?? undefined;
|
|
1769
1773
|
const effectiveMode = resolveOpenCodeRuntimeAgentId(this.currentMode);
|
|
1770
|
-
const turnId = this.createTurnId();
|
|
1771
|
-
this.activeForegroundTurnId = turnId;
|
|
1772
|
-
// OpenCode's /event SSE endpoint does NOT replay past events. If we send
|
|
1773
|
-
// the prompt before our reader is connected, terminal events fired early
|
|
1774
|
-
// by the server (e.g. session.error / session.idle for invalid model or
|
|
1775
|
-
// mode) are missed and the turn hangs forever. Wait for the subscription
|
|
1776
|
-
// to be established before sending anything.
|
|
1777
|
-
const subscriptionReady = createDeferred();
|
|
1778
|
-
void this.consumeEventStream(turnId, turnAbortController, subscriptionReady);
|
|
1779
1774
|
try {
|
|
1780
|
-
await
|
|
1775
|
+
await this.ensureEventStreamReady();
|
|
1781
1776
|
}
|
|
1782
|
-
catch {
|
|
1783
|
-
|
|
1784
|
-
|
|
1777
|
+
catch (error) {
|
|
1778
|
+
if (this.abortController === turnAbortController) {
|
|
1779
|
+
this.abortController = null;
|
|
1780
|
+
}
|
|
1781
|
+
throw error;
|
|
1785
1782
|
}
|
|
1783
|
+
const turnId = this.createTurnId();
|
|
1784
|
+
this.activeForegroundTurnId = turnId;
|
|
1786
1785
|
this.notifySubscribers({ type: "turn_started", provider: "opencode" }, turnId);
|
|
1787
1786
|
const slashCommand = await this.resolveSlashCommandInvocation(prompt);
|
|
1788
1787
|
if (slashCommand) {
|
|
@@ -1815,12 +1814,8 @@ class OpenCodeAgentSession {
|
|
|
1815
1814
|
});
|
|
1816
1815
|
return { turnId };
|
|
1817
1816
|
}
|
|
1818
|
-
// command()
|
|
1819
|
-
//
|
|
1820
|
-
// our SSE reader connects, we miss `session.idle` and the turn hangs.
|
|
1821
|
-
// Handle both success and error in the response handler as a fallback —
|
|
1822
|
-
// finishForegroundTurn's guard prevents duplicate terminal events if the
|
|
1823
|
-
// SSE stream already delivered the event.
|
|
1817
|
+
// command() is only dispatch acknowledgement. OpenCode session events are
|
|
1818
|
+
// the source of truth for when the command turn becomes idle or fails.
|
|
1824
1819
|
void this.client.session
|
|
1825
1820
|
.command({
|
|
1826
1821
|
sessionID: this.sessionId,
|
|
@@ -1844,9 +1839,6 @@ class OpenCodeAgentSession {
|
|
|
1844
1839
|
const errorMsg = toDiagnosticErrorMessage(response.error);
|
|
1845
1840
|
this.finishForegroundTurn({ type: "turn_failed", provider: "opencode", error: errorMsg }, turnId);
|
|
1846
1841
|
}
|
|
1847
|
-
else {
|
|
1848
|
-
this.finishForegroundTurn({ type: "turn_completed", provider: "opencode", usage: undefined }, turnId);
|
|
1849
|
-
}
|
|
1850
1842
|
return;
|
|
1851
1843
|
})
|
|
1852
1844
|
.catch((err) => {
|
|
@@ -1929,89 +1921,95 @@ class OpenCodeAgentSession {
|
|
|
1929
1921
|
this.subscribers.delete(callback);
|
|
1930
1922
|
};
|
|
1931
1923
|
}
|
|
1932
|
-
|
|
1924
|
+
startEventStream() {
|
|
1925
|
+
void this.ensureEventStreamReady().catch((error) => {
|
|
1926
|
+
this.logger.warn({ err: error, sessionId: this.sessionId }, "OpenCode event stream failed");
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
ensureEventStreamReady() {
|
|
1930
|
+
if (this.eventStreamReady) {
|
|
1931
|
+
return this.eventStreamReady.promise;
|
|
1932
|
+
}
|
|
1933
|
+
const eventStreamAbortController = new AbortController();
|
|
1934
|
+
const eventStreamReady = createDeferred();
|
|
1935
|
+
this.eventStreamAbortController = eventStreamAbortController;
|
|
1936
|
+
this.eventStreamReady = eventStreamReady;
|
|
1937
|
+
void this.consumeEventStream(eventStreamAbortController, eventStreamReady).finally(() => {
|
|
1938
|
+
if (this.eventStreamAbortController === eventStreamAbortController) {
|
|
1939
|
+
this.eventStreamAbortController = null;
|
|
1940
|
+
this.eventStreamReady = null;
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
return eventStreamReady.promise;
|
|
1944
|
+
}
|
|
1945
|
+
async consumeEventStream(eventStreamAbortController, eventStreamReady) {
|
|
1933
1946
|
this.traceOpenCode("provider.opencode.subscribe.start", {
|
|
1934
|
-
turnId,
|
|
1935
1947
|
sessionId: this.sessionId,
|
|
1936
1948
|
cwd: this.config.cwd,
|
|
1937
1949
|
});
|
|
1950
|
+
let eventStreamReadyResolved = false;
|
|
1938
1951
|
try {
|
|
1939
1952
|
const result = await this.client.global.event({
|
|
1940
|
-
signal:
|
|
1953
|
+
signal: eventStreamAbortController.signal,
|
|
1941
1954
|
sseMaxRetryAttempts: 0,
|
|
1942
1955
|
});
|
|
1956
|
+
eventStreamReadyResolved = true;
|
|
1957
|
+
this.traceOpenCode("provider.opencode.subscribe.ready", {
|
|
1958
|
+
sessionId: this.sessionId,
|
|
1959
|
+
});
|
|
1960
|
+
eventStreamReady.resolve();
|
|
1943
1961
|
let eventCount = 0;
|
|
1944
|
-
let subscriptionReadyResolved = false;
|
|
1945
1962
|
for await (const rawEvent of result.stream) {
|
|
1946
1963
|
eventCount += 1;
|
|
1947
|
-
|
|
1948
|
-
subscriptionReadyResolved = true;
|
|
1949
|
-
this.traceOpenCode("provider.opencode.subscribe.ready", {
|
|
1950
|
-
turnId,
|
|
1951
|
-
sessionId: this.sessionId,
|
|
1952
|
-
});
|
|
1953
|
-
subscriptionReady.resolve();
|
|
1954
|
-
}
|
|
1955
|
-
const shouldContinue = await this.consumeOpenCodeStreamEvent({
|
|
1956
|
-
rawEvent,
|
|
1957
|
-
eventCount,
|
|
1958
|
-
turnId,
|
|
1959
|
-
turnAbortController,
|
|
1960
|
-
});
|
|
1961
|
-
if (!shouldContinue) {
|
|
1962
|
-
return;
|
|
1963
|
-
}
|
|
1964
|
+
await this.consumeOpenCodeStreamEvent({ rawEvent, eventCount });
|
|
1964
1965
|
}
|
|
1965
1966
|
this.traceOpenCode("provider.opencode.stream.eof", {
|
|
1966
|
-
turnId,
|
|
1967
1967
|
eventCount,
|
|
1968
|
-
aborted:
|
|
1969
|
-
|
|
1968
|
+
aborted: eventStreamAbortController.signal.aborted,
|
|
1969
|
+
activeTurnId: this.activeForegroundTurnId,
|
|
1970
1970
|
});
|
|
1971
|
-
if (!
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1971
|
+
if (!eventStreamAbortController.signal.aborted) {
|
|
1972
|
+
if (!eventStreamReadyResolved) {
|
|
1973
|
+
eventStreamReady.reject(new Error("OpenCode event stream ended before it became ready"));
|
|
1974
|
+
}
|
|
1975
|
+
const activeTurnId = this.activeForegroundTurnId;
|
|
1976
|
+
if (activeTurnId) {
|
|
1977
|
+
this.traceOpenCode("provider.opencode.turn.fail_eof", {
|
|
1978
|
+
turnId: activeTurnId,
|
|
1979
|
+
eventCount,
|
|
1980
|
+
});
|
|
1981
|
+
this.finishForegroundTurn({
|
|
1982
|
+
type: "turn_failed",
|
|
1983
|
+
provider: "opencode",
|
|
1984
|
+
error: "OpenCode event stream ended before the turn reached a terminal state",
|
|
1985
|
+
}, activeTurnId);
|
|
1975
1986
|
}
|
|
1976
|
-
this.finishForegroundTurn({
|
|
1977
|
-
type: "turn_failed",
|
|
1978
|
-
provider: "opencode",
|
|
1979
|
-
error: "OpenCode event stream ended before the turn reached a terminal state",
|
|
1980
|
-
}, turnId);
|
|
1981
1987
|
}
|
|
1982
1988
|
}
|
|
1983
1989
|
catch (error) {
|
|
1984
1990
|
this.traceOpenCode("provider.opencode.subscribe.error", {
|
|
1985
|
-
turnId,
|
|
1991
|
+
turnId: this.activeForegroundTurnId ?? undefined,
|
|
1986
1992
|
error: error instanceof Error ? { name: error.name, message: error.message } : String(error),
|
|
1987
1993
|
});
|
|
1988
|
-
|
|
1989
|
-
|
|
1994
|
+
if (!eventStreamReadyResolved) {
|
|
1995
|
+
eventStreamReady.reject(error);
|
|
1996
|
+
}
|
|
1997
|
+
const activeTurnId = this.activeForegroundTurnId;
|
|
1998
|
+
if (!eventStreamAbortController.signal.aborted && activeTurnId) {
|
|
1990
1999
|
this.finishForegroundTurn({
|
|
1991
2000
|
type: "turn_failed",
|
|
1992
2001
|
provider: "opencode",
|
|
1993
2002
|
error: toDiagnosticErrorMessage(error),
|
|
1994
|
-
},
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
finally {
|
|
1998
|
-
if (turnAbortController.signal.aborted) {
|
|
1999
|
-
this.finishForegroundTurn({
|
|
2000
|
-
type: "turn_canceled",
|
|
2001
|
-
provider: "opencode",
|
|
2002
|
-
reason: "interrupted",
|
|
2003
|
-
}, turnId);
|
|
2004
|
-
}
|
|
2005
|
-
if (this.abortController === turnAbortController && this.activeForegroundTurnId !== turnId) {
|
|
2006
|
-
this.abortController = null;
|
|
2003
|
+
}, activeTurnId);
|
|
2007
2004
|
}
|
|
2008
2005
|
}
|
|
2009
2006
|
}
|
|
2010
2007
|
async consumeOpenCodeStreamEvent(params) {
|
|
2011
|
-
const { rawEvent, eventCount
|
|
2008
|
+
const { rawEvent, eventCount } = params;
|
|
2009
|
+
const turnId = this.activeForegroundTurnId;
|
|
2012
2010
|
const event = unwrapOpenCodeGlobalEvent(rawEvent);
|
|
2013
2011
|
this.traceOpenCode("provider.opencode.raw_event", {
|
|
2014
|
-
turnId,
|
|
2012
|
+
turnId: turnId ?? undefined,
|
|
2015
2013
|
n: eventCount,
|
|
2016
2014
|
type: event?.type,
|
|
2017
2015
|
rawType: readOpenCodeRecord(rawEvent)?.type,
|
|
@@ -2020,16 +2018,15 @@ class OpenCodeAgentSession {
|
|
|
2020
2018
|
properties: event?.properties,
|
|
2021
2019
|
});
|
|
2022
2020
|
if (!event) {
|
|
2023
|
-
return
|
|
2021
|
+
return;
|
|
2024
2022
|
}
|
|
2025
|
-
if (
|
|
2023
|
+
if (!turnId) {
|
|
2026
2024
|
this.traceOpenCode("provider.opencode.event.skip", {
|
|
2027
|
-
turnId,
|
|
2028
2025
|
n: eventCount,
|
|
2029
|
-
|
|
2030
|
-
|
|
2026
|
+
reason: "no_active_turn",
|
|
2027
|
+
type: event.type,
|
|
2031
2028
|
});
|
|
2032
|
-
return
|
|
2029
|
+
return;
|
|
2033
2030
|
}
|
|
2034
2031
|
const translated = await this.translateEvent(event);
|
|
2035
2032
|
this.traceOpenCode("provider.opencode.parsed_event", {
|
|
@@ -2042,7 +2039,7 @@ class OpenCodeAgentSession {
|
|
|
2042
2039
|
for (const e of translated) {
|
|
2043
2040
|
if (this.activeForegroundTurnId !== turnId) {
|
|
2044
2041
|
this.traceOpenCode("provider.opencode.parsed_event.skip_active", { turnId, type: e.type });
|
|
2045
|
-
return
|
|
2042
|
+
return;
|
|
2046
2043
|
}
|
|
2047
2044
|
if (e.type === "timeline" && e.item.type === "tool_call") {
|
|
2048
2045
|
this.trackToolCall(e.item);
|
|
@@ -2054,11 +2051,10 @@ class OpenCodeAgentSession {
|
|
|
2054
2051
|
type: terminalEvent.type,
|
|
2055
2052
|
});
|
|
2056
2053
|
this.finishForegroundTurn(terminalEvent, turnId);
|
|
2057
|
-
return
|
|
2054
|
+
return;
|
|
2058
2055
|
}
|
|
2059
2056
|
this.notifySubscribers(e, turnId);
|
|
2060
2057
|
}
|
|
2061
|
-
return true;
|
|
2062
2058
|
}
|
|
2063
2059
|
finishForegroundTurn(event, turnId) {
|
|
2064
2060
|
this.traceOpenCode("provider.opencode.finish_foreground_turn", {
|
|
@@ -2078,8 +2074,6 @@ class OpenCodeAgentSession {
|
|
|
2078
2074
|
this.runningToolCalls.clear();
|
|
2079
2075
|
}
|
|
2080
2076
|
this.activeForegroundTurnId = null;
|
|
2081
|
-
// Abort the SSE connection so the SDK tears down the underlying fetch.
|
|
2082
|
-
this.abortController?.abort();
|
|
2083
2077
|
this.abortController = null;
|
|
2084
2078
|
this.notifySubscribers(event, turnId);
|
|
2085
2079
|
}
|
|
@@ -2114,6 +2108,9 @@ class OpenCodeAgentSession {
|
|
|
2114
2108
|
this.runningToolCalls.clear();
|
|
2115
2109
|
}
|
|
2116
2110
|
notifySubscribers(event, turnIdOverride) {
|
|
2111
|
+
if (this.closed) {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2117
2114
|
const turnId = turnIdOverride ?? this.activeForegroundTurnId;
|
|
2118
2115
|
const tagged = turnId ? { ...event, turnId } : event;
|
|
2119
2116
|
this.traceOpenCode("provider.opencode.event_emit", {
|
|
@@ -2149,39 +2146,9 @@ class OpenCodeAgentSession {
|
|
|
2149
2146
|
if (response.error || !response.data) {
|
|
2150
2147
|
return;
|
|
2151
2148
|
}
|
|
2152
|
-
for (const
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
.filter((p) => p.type === "text")
|
|
2156
|
-
.map((p) => p.text)
|
|
2157
|
-
.join("");
|
|
2158
|
-
if (text) {
|
|
2159
|
-
yield buildOpenCodeReplayTimelineEvent({
|
|
2160
|
-
item: { type: "user_message", text },
|
|
2161
|
-
message: info,
|
|
2162
|
-
});
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
else {
|
|
2166
|
-
let emittedAssistantText = false;
|
|
2167
|
-
for (const part of parts) {
|
|
2168
|
-
if (part.type === "text" && part.text) {
|
|
2169
|
-
emittedAssistantText = true;
|
|
2170
|
-
}
|
|
2171
|
-
const event = buildOpenCodeReplayPartTimelineEvent({ part, message: info });
|
|
2172
|
-
if (event) {
|
|
2173
|
-
yield event;
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
if (!emittedAssistantText) {
|
|
2177
|
-
const text = stringifyStructuredAssistantMessage(info.structured);
|
|
2178
|
-
if (text) {
|
|
2179
|
-
yield buildOpenCodeReplayTimelineEvent({
|
|
2180
|
-
item: { type: "assistant_message", text },
|
|
2181
|
-
message: info,
|
|
2182
|
-
});
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2149
|
+
for (const message of response.data) {
|
|
2150
|
+
for (const event of buildOpenCodeReplayTimelineEvents(message)) {
|
|
2151
|
+
yield event;
|
|
2185
2152
|
}
|
|
2186
2153
|
}
|
|
2187
2154
|
}
|
|
@@ -2207,21 +2174,7 @@ class OpenCodeAgentSession {
|
|
|
2207
2174
|
return this.currentMode;
|
|
2208
2175
|
}
|
|
2209
2176
|
async listCommands() {
|
|
2210
|
-
|
|
2211
|
-
directory: this.config.cwd,
|
|
2212
|
-
});
|
|
2213
|
-
const commandsByName = new Map(OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS.map((command) => [command.name, command]));
|
|
2214
|
-
if (result.error || !result.data) {
|
|
2215
|
-
return Array.from(commandsByName.values());
|
|
2216
|
-
}
|
|
2217
|
-
for (const cmd of result.data) {
|
|
2218
|
-
commandsByName.set(cmd.name, {
|
|
2219
|
-
name: cmd.name,
|
|
2220
|
-
description: cmd.description ?? "",
|
|
2221
|
-
argumentHint: cmd.hints?.length ? cmd.hints.join(" ") : "",
|
|
2222
|
-
});
|
|
2223
|
-
}
|
|
2224
|
-
return Array.from(commandsByName.values());
|
|
2177
|
+
return await listOpenCodeCommandsFromSdk(this.client, this.config.cwd);
|
|
2225
2178
|
}
|
|
2226
2179
|
async setMode(modeId) {
|
|
2227
2180
|
this.currentMode = normalizeOpenCodeModeId(modeId);
|
|
@@ -2280,12 +2233,23 @@ class OpenCodeAgentSession {
|
|
|
2280
2233
|
nativeHandle: this.sessionId,
|
|
2281
2234
|
metadata: {
|
|
2282
2235
|
cwd: this.config.cwd,
|
|
2236
|
+
...(this.config.modeId ? { modeId: this.config.modeId } : {}),
|
|
2237
|
+
...(this.config.model ? { model: this.config.model } : {}),
|
|
2283
2238
|
},
|
|
2284
2239
|
};
|
|
2285
2240
|
}
|
|
2286
2241
|
async close() {
|
|
2287
2242
|
try {
|
|
2243
|
+
// Flip closed before clearing subscribers so any event the SDK delivers
|
|
2244
|
+
// after the abort (between here and subscribers.clear) is swallowed by
|
|
2245
|
+
// notifySubscribers instead of bubbling through provider-runner as an
|
|
2246
|
+
// unhandled rejection in whichever test the daemon hops to next.
|
|
2247
|
+
this.closed = true;
|
|
2288
2248
|
this.abortController?.abort();
|
|
2249
|
+
this.eventStreamAbortController?.abort();
|
|
2250
|
+
this.eventStreamAbortController = null;
|
|
2251
|
+
this.eventStreamReady = null;
|
|
2252
|
+
this.subscribers.clear();
|
|
2289
2253
|
await reconcileOpenCodeSessionClose({
|
|
2290
2254
|
client: this.client,
|
|
2291
2255
|
sessionId: this.sessionId,
|
|
@@ -2293,7 +2257,6 @@ class OpenCodeAgentSession {
|
|
|
2293
2257
|
logger: this.logger,
|
|
2294
2258
|
});
|
|
2295
2259
|
await this.deleteProviderSessionIfEphemeral();
|
|
2296
|
-
this.subscribers.clear();
|
|
2297
2260
|
this.activeForegroundTurnId = null;
|
|
2298
2261
|
}
|
|
2299
2262
|
finally {
|