@opencode/client 2.0.2 → 2.0.4
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/effect/api/api.d.ts +331 -345
- package/dist/effect/api.d.ts +1 -5
- package/dist/effect/client.d.ts +60 -69
- package/dist/effect/generated/client.d.ts +61 -70
- package/dist/effect/generated/client.js +58 -91
- package/dist/effect/index.d.ts +1 -1
- package/dist/effect/service.js +43 -32
- package/dist/promise/api.d.ts +0 -4
- package/dist/promise/client.d.ts +23 -39
- package/dist/promise/generated/client.d.ts +24 -40
- package/dist/promise/generated/client.js +125 -192
- package/dist/promise/generated/types.d.ts +1421 -1872
- package/dist/promise/generated/types.js +6 -3
- package/dist/promise/index.d.ts +1 -1
- package/dist/promise/service.js +40 -24
- package/dist/rpc-runtime.d.ts +0 -1
- package/dist/solid/data.d.ts +4 -4
- package/dist/solid/data.js +79 -42
- package/dist/solid/pty.js +0 -2
- package/package.json +4 -4
|
@@ -15,12 +15,15 @@ export const isInstructionEntryValueTooLargeError = (value) => typeof value ===
|
|
|
15
15
|
value !== null &&
|
|
16
16
|
"_tag" in value &&
|
|
17
17
|
value["_tag"] === "InstructionEntryValueTooLargeError";
|
|
18
|
-
export const isProviderNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProviderNotFoundError";
|
|
19
|
-
export const isMcpServerNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "McpServerNotFoundError";
|
|
20
|
-
export const isProjectNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProjectNotFoundError";
|
|
21
18
|
export const isFormNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FormNotFoundError";
|
|
22
19
|
export const isFormInvalidAnswerError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FormInvalidAnswerError";
|
|
23
20
|
export const isFormAlreadySettledError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FormAlreadySettledError";
|
|
21
|
+
export const isProviderNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProviderNotFoundError";
|
|
22
|
+
export const isIntegrationNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationNotFoundError";
|
|
23
|
+
export const isIntegrationAttemptNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationAttemptNotFoundError";
|
|
24
|
+
export const isIntegrationMethodNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationMethodNotFoundError";
|
|
25
|
+
export const isMcpServerNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "McpServerNotFoundError";
|
|
26
|
+
export const isProjectNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProjectNotFoundError";
|
|
24
27
|
export const isPermissionNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "PermissionNotFoundError";
|
|
25
28
|
export const isFileNotFoundError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FileNotFoundError";
|
|
26
29
|
export const isRpcError = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "RpcError";
|
package/dist/promise/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OpenCode } from "./client.js";
|
|
2
2
|
export * from "./generated/index.js";
|
|
3
3
|
export { OpenCode } from "./client.js";
|
|
4
|
-
export type { AgentApi,
|
|
4
|
+
export type { AgentApi, CommandApi, ConfigApi, EventApi, IntegrationApi, ModelApi, PluginApi, ProviderApi, ReferenceApi, RpcApi, RpcCallOptions, RpcClient, RpcEventPayload, WebSearchApi, SessionApi, SkillApi, } from "./api.js";
|
|
5
5
|
export type { EventSubscribeOutput as OpenCodeEvent } from "./generated/types.js";
|
|
6
6
|
export type OpenCodeClient = ReturnType<typeof OpenCode.make>;
|
package/dist/promise/service.js
CHANGED
|
@@ -16,6 +16,8 @@ export async function discover(options = {}) {
|
|
|
16
16
|
const found = (await registered(options.file)).service;
|
|
17
17
|
if (found?.state !== "ready")
|
|
18
18
|
return undefined;
|
|
19
|
+
if (!found.compatible)
|
|
20
|
+
return undefined;
|
|
19
21
|
if (!matchesVersion(found.version, options))
|
|
20
22
|
return undefined;
|
|
21
23
|
return found.endpoint;
|
|
@@ -50,7 +52,7 @@ export async function ensure(options = {}) {
|
|
|
50
52
|
while (true) {
|
|
51
53
|
if (Date.now() >= deadline)
|
|
52
54
|
throw new Error("Timed out waiting for the background service to start");
|
|
53
|
-
const registration = await registered(options.file,
|
|
55
|
+
const registration = await registered(options.file, timing.requestTimeout);
|
|
54
56
|
if (registration.timedOut && registration.info !== undefined) {
|
|
55
57
|
timeouts = {
|
|
56
58
|
info: registration.info,
|
|
@@ -70,7 +72,7 @@ export async function ensure(options = {}) {
|
|
|
70
72
|
if (registration.service !== undefined) {
|
|
71
73
|
spawnDelay = timing.spawnDelay;
|
|
72
74
|
const service = registration.service;
|
|
73
|
-
const compatible =
|
|
75
|
+
const compatible = service.compatible && matchesVersion(service.version, options);
|
|
74
76
|
if (compatible && service.state === "ready") {
|
|
75
77
|
await PtyHandoff.complete(options.file ?? fallback(), service.info);
|
|
76
78
|
return service.endpoint;
|
|
@@ -79,11 +81,11 @@ export async function ensure(options = {}) {
|
|
|
79
81
|
throw new Error("Background service failed to start");
|
|
80
82
|
if (!compatible) {
|
|
81
83
|
announce("version-mismatch", service.version);
|
|
82
|
-
if (
|
|
84
|
+
if (service.state !== "ready")
|
|
83
85
|
console.warn("Background service is not ready; replacement cannot preserve persistent terminals");
|
|
84
86
|
await stop({
|
|
85
87
|
file: options.file,
|
|
86
|
-
pty:
|
|
88
|
+
pty: service.state === "ready" ? "handoff" : "clear",
|
|
87
89
|
}).catch(() => undefined);
|
|
88
90
|
lastSpawn = 0;
|
|
89
91
|
}
|
|
@@ -145,7 +147,7 @@ async function read(file) {
|
|
|
145
147
|
return undefined;
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
|
-
async function probeResult(info,
|
|
150
|
+
async function probeResult(info, timeout = defaultEnsureTiming.requestTimeout) {
|
|
149
151
|
const endpoint = {
|
|
150
152
|
url: info.url,
|
|
151
153
|
auth: info.password === undefined
|
|
@@ -153,47 +155,61 @@ async function probeResult(info, allowLegacy = false, timeout = defaultEnsureTim
|
|
|
153
155
|
: { type: "basic", username: "opencode", password: info.password },
|
|
154
156
|
};
|
|
155
157
|
const signal = AbortSignal.timeout(timeout);
|
|
156
|
-
const result = await fetch(new URL("/api/
|
|
157
|
-
headers: headers(endpoint),
|
|
158
|
-
signal,
|
|
159
|
-
})
|
|
158
|
+
const result = await fetch(new URL("/api/status", info.url), { headers: headers(endpoint), signal })
|
|
160
159
|
.then(async (response) => ({
|
|
161
160
|
response,
|
|
162
|
-
body: (await response.json()),
|
|
161
|
+
body: response.status === 404 ? undefined : (await response.json()),
|
|
163
162
|
}))
|
|
164
163
|
.then((value) => ({ value }), (cause) => ({ cause }));
|
|
165
164
|
if ("cause" in result)
|
|
166
165
|
return { service: undefined, timedOut: signal.aborted };
|
|
167
166
|
const response = result.value.response;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
// The previous V2 service exposes /api/health instead. Its authenticated 404 is enough
|
|
168
|
+
// to recognize the registered daemon as incompatible and route it through replacement.
|
|
169
|
+
if (response.status === 404)
|
|
170
|
+
return {
|
|
171
|
+
service: {
|
|
172
|
+
info,
|
|
173
|
+
endpoint,
|
|
174
|
+
version: info.version,
|
|
175
|
+
state: "ready",
|
|
176
|
+
compatible: false,
|
|
177
|
+
},
|
|
178
|
+
timedOut: false,
|
|
179
|
+
};
|
|
180
|
+
const status = decodeStatus(result.value.body);
|
|
181
|
+
if (status !== undefined) {
|
|
182
|
+
if (status.pid !== info.pid)
|
|
171
183
|
return { service: undefined, timedOut: false };
|
|
172
|
-
if (info.version !== undefined &&
|
|
184
|
+
if (info.version !== undefined && status.version !== info.version)
|
|
173
185
|
return { service: undefined, timedOut: false };
|
|
174
186
|
return {
|
|
175
187
|
service: {
|
|
176
188
|
info,
|
|
177
189
|
endpoint,
|
|
178
|
-
version:
|
|
190
|
+
version: status.version,
|
|
179
191
|
state: response.ok ? "ready" : response.status === 500 ? "failed" : "waiting",
|
|
180
|
-
|
|
192
|
+
compatible: true,
|
|
181
193
|
},
|
|
182
194
|
timedOut: false,
|
|
183
195
|
};
|
|
184
196
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
197
|
+
return { service: undefined, timedOut: false };
|
|
198
|
+
}
|
|
199
|
+
function decodeStatus(input) {
|
|
200
|
+
if (typeof input !== "object" || input === null)
|
|
201
|
+
return;
|
|
202
|
+
if (!("version" in input) || typeof input.version !== "string")
|
|
203
|
+
return;
|
|
204
|
+
if (!("pid" in input) || typeof input.pid !== "number" || !Number.isInteger(input.pid) || input.pid < 0)
|
|
205
|
+
return;
|
|
206
|
+
return { version: input.version, pid: input.pid };
|
|
191
207
|
}
|
|
192
|
-
async function registered(file,
|
|
208
|
+
async function registered(file, timeout) {
|
|
193
209
|
const info = await read(file);
|
|
194
210
|
if (info === undefined)
|
|
195
211
|
return { info: undefined, service: undefined, timedOut: false };
|
|
196
|
-
return { info, ...(await probeResult(info,
|
|
212
|
+
return { info, ...(await probeResult(info, timeout)) };
|
|
197
213
|
}
|
|
198
214
|
function signal(pid, name) {
|
|
199
215
|
try {
|
package/dist/rpc-runtime.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export declare const event: <D extends Rpc.Definition, Name extends keyof D["eve
|
|
|
7
7
|
readonly id: string & import("effect/Brand").Brand<"Event.ID">;
|
|
8
8
|
readonly location: {
|
|
9
9
|
readonly directory: string & import("effect/Brand").Brand<"AbsolutePath">;
|
|
10
|
-
readonly workspaceID?: (string & import("effect/Brand").Brand<"Workspace.ID">) | undefined;
|
|
11
10
|
};
|
|
12
11
|
readonly data: {
|
|
13
12
|
readonly [x: string]: unknown;
|
package/dist/solid/data.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentInfo, CommandInfo, ConfigEntry,
|
|
1
|
+
import type { AgentInfo, CommandInfo, ConfigEntry, SessionFormCancelInput, FormInfo, SessionFormReplyInput, IntegrationInfo, LocationRef, McpResource, McpServer, ModelInfo, ModelRef, PermissionSavedInfo, PermissionRequest, PermissionReplyInput, Project, ProviderInfo, ReferenceInfo, SessionMessageInfo, SessionInfo, SessionInboxInfo, SessionInboxCompaction, ShellInfo, SkillInfo, VcsInfo, OpenCodeEvent, OpenCodeClient, WebSearchProvider } from "../promise";
|
|
2
2
|
import { type SessionPromptInput } from "../promise";
|
|
3
3
|
export type DataSessionStatus = "idle" | "running";
|
|
4
4
|
type OpenCodeEventMap = {
|
|
@@ -106,8 +106,8 @@ export declare function createData(config: CreateDataInput): {
|
|
|
106
106
|
list(sessionID: string, ref?: LocationRef): FormWithLocation[] | undefined;
|
|
107
107
|
sync(sessionID: string, ref?: LocationRef): Promise<void>;
|
|
108
108
|
invalidate(sessionID: string, ref?: LocationRef): void;
|
|
109
|
-
reply(input:
|
|
110
|
-
cancel(input:
|
|
109
|
+
reply(input: SessionFormReplyInput, ref?: LocationRef): Promise<void>;
|
|
110
|
+
cancel(input: SessionFormCancelInput, ref?: LocationRef): Promise<void>;
|
|
111
111
|
};
|
|
112
112
|
};
|
|
113
113
|
project: {
|
|
@@ -129,7 +129,7 @@ export declare function createData(config: CreateDataInput): {
|
|
|
129
129
|
invalidate: (ref?: LocationRef) => void;
|
|
130
130
|
};
|
|
131
131
|
location: {
|
|
132
|
-
info(ref?: LocationRef):
|
|
132
|
+
info(ref?: LocationRef): import("../promise").LocationPublicInfo | undefined;
|
|
133
133
|
default(): LocationRef;
|
|
134
134
|
syncInfo(ref?: LocationRef): Promise<void>;
|
|
135
135
|
sync(ref?: LocationRef): Promise<void>;
|
package/dist/solid/data.js
CHANGED
|
@@ -16,7 +16,7 @@ export function locationKey(location) {
|
|
|
16
16
|
return JSON.stringify([location.directory, location.workspaceID]);
|
|
17
17
|
}
|
|
18
18
|
function locationQuery(ref) {
|
|
19
|
-
return { directory: ref.directory
|
|
19
|
+
return { directory: ref.directory };
|
|
20
20
|
}
|
|
21
21
|
function formRequestOptions(sessionID, ref) {
|
|
22
22
|
if (sessionID !== "global" || !ref)
|
|
@@ -24,7 +24,6 @@ function formRequestOptions(sessionID, ref) {
|
|
|
24
24
|
return {
|
|
25
25
|
headers: {
|
|
26
26
|
"x-opencode-directory": encodeURIComponent(ref.directory),
|
|
27
|
-
...(ref.workspaceID ? { "x-opencode-workspace": ref.workspaceID } : {}),
|
|
28
27
|
},
|
|
29
28
|
};
|
|
30
29
|
}
|
|
@@ -137,6 +136,7 @@ export function createData(config) {
|
|
|
137
136
|
const messageIndex = new Map();
|
|
138
137
|
const sync = createSync();
|
|
139
138
|
let activeUpdates;
|
|
139
|
+
const pendingUpdates = new Map();
|
|
140
140
|
function setSessionActive(sessionID, status) {
|
|
141
141
|
activeUpdates?.set(sessionID, status);
|
|
142
142
|
setStore("session", "active", sessionID, status);
|
|
@@ -144,6 +144,7 @@ export function createData(config) {
|
|
|
144
144
|
function removePending(sessionID, inboxID) {
|
|
145
145
|
if (!inboxID)
|
|
146
146
|
return;
|
|
147
|
+
pendingUpdates.get(sessionID)?.set(inboxID, undefined);
|
|
147
148
|
if (store.session.pending[sessionID]?.some((item) => item.id === inboxID))
|
|
148
149
|
setStore("session", "pending", sessionID, (store.session.pending[sessionID] ?? []).filter((item) => item.id !== inboxID));
|
|
149
150
|
}
|
|
@@ -186,6 +187,7 @@ export function createData(config) {
|
|
|
186
187
|
function updatePending(sessionID, inboxID, delivery) {
|
|
187
188
|
const index = store.session.pending[sessionID]?.findIndex((item) => item.id === inboxID) ?? -1;
|
|
188
189
|
const item = store.session.pending[sessionID]?.[index];
|
|
190
|
+
pendingUpdates.get(sessionID)?.set(inboxID, item ? { ...item, delivery } : delivery);
|
|
189
191
|
if (index < 0 || !item || item.delivery === delivery)
|
|
190
192
|
return;
|
|
191
193
|
setStore("session", "pending", sessionID, index, { ...item, delivery });
|
|
@@ -252,8 +254,8 @@ export function createData(config) {
|
|
|
252
254
|
return;
|
|
253
255
|
message.update(item.sessionID, (draft, index) => {
|
|
254
256
|
const row = item.type === "user"
|
|
255
|
-
? { id: item.id, type: "user", ...item.payload, time: { created: item.
|
|
256
|
-
: { id: item.id, type: "synthetic", ...item.payload, time: { created: item.
|
|
257
|
+
? { id: item.id, type: "user", ...item.payload, time: { created: item.time.created } }
|
|
258
|
+
: { id: item.id, type: "synthetic", ...item.payload, time: { created: item.time.created } };
|
|
257
259
|
const position = index.get(item.id);
|
|
258
260
|
if (position === undefined)
|
|
259
261
|
return message.append(draft, index, row);
|
|
@@ -527,7 +529,7 @@ export function createData(config) {
|
|
|
527
529
|
time: { created: event.created },
|
|
528
530
|
});
|
|
529
531
|
refresh(() => api()
|
|
530
|
-
.session.message({ sessionID: event.data.sessionID, messageID: messageIDFromEvent(event.id) })
|
|
532
|
+
.session.message.get({ sessionID: event.data.sessionID, messageID: messageIDFromEvent(event.id) })
|
|
531
533
|
.then((item) => {
|
|
532
534
|
message.update(event.data.sessionID, (draft, index) => {
|
|
533
535
|
const position = index.get(item.id);
|
|
@@ -550,7 +552,7 @@ export function createData(config) {
|
|
|
550
552
|
});
|
|
551
553
|
return;
|
|
552
554
|
}
|
|
553
|
-
case "session.permissions
|
|
555
|
+
case "session.permissions":
|
|
554
556
|
if (store.session.info[event.data.sessionID])
|
|
555
557
|
setStore("session", "info", event.data.sessionID, "permissions", event.data.permissions);
|
|
556
558
|
return;
|
|
@@ -583,8 +585,6 @@ export function createData(config) {
|
|
|
583
585
|
const explicit = event.data.adopted?.includes(info.projectID);
|
|
584
586
|
const directory = explicit ? store.project.info[info.projectID]?.canonical : info.location.directory;
|
|
585
587
|
if (!directory) {
|
|
586
|
-
if (info.location.workspaceID)
|
|
587
|
-
continue;
|
|
588
588
|
result.session.invalidate(sessionID);
|
|
589
589
|
refresh(() => result.session.sync(sessionID));
|
|
590
590
|
continue;
|
|
@@ -592,7 +592,6 @@ export function createData(config) {
|
|
|
592
592
|
const adopted = Worktree.adopt({
|
|
593
593
|
projectID: info.projectID,
|
|
594
594
|
directory,
|
|
595
|
-
workspaceID: info.location.workspaceID,
|
|
596
595
|
}, event.data);
|
|
597
596
|
if (!adopted)
|
|
598
597
|
continue;
|
|
@@ -629,12 +628,14 @@ export function createData(config) {
|
|
|
629
628
|
}
|
|
630
629
|
case "session.inbox.enqueued": {
|
|
631
630
|
outbox.delete(event.data.inboxID);
|
|
632
|
-
|
|
631
|
+
const item = {
|
|
633
632
|
id: event.data.inboxID,
|
|
634
633
|
sessionID: event.data.sessionID,
|
|
635
|
-
|
|
634
|
+
time: { created: event.created },
|
|
636
635
|
...event.data.item,
|
|
637
|
-
}
|
|
636
|
+
};
|
|
637
|
+
pendingUpdates.get(item.sessionID)?.set(item.id, item);
|
|
638
|
+
admitLocal(item);
|
|
638
639
|
if (event.data.item.type === "compaction") {
|
|
639
640
|
const active = compacting.get(event.data.sessionID);
|
|
640
641
|
active?.observed.add(event.data.inboxID);
|
|
@@ -898,6 +899,17 @@ export function createData(config) {
|
|
|
898
899
|
});
|
|
899
900
|
if (event.type === "session.execution.interrupted" && event.data.reason === "shutdown")
|
|
900
901
|
return;
|
|
902
|
+
// Mirror the projected idle marker so turn boundaries match before the next message read.
|
|
903
|
+
message.insert(event.data.sessionID, {
|
|
904
|
+
id: messageIDFromEvent(event.id),
|
|
905
|
+
type: "idle",
|
|
906
|
+
outcome: event.type === "session.execution.succeeded"
|
|
907
|
+
? "succeeded"
|
|
908
|
+
: event.type === "session.execution.failed"
|
|
909
|
+
? "failed"
|
|
910
|
+
: "interrupted",
|
|
911
|
+
time: { created: event.created },
|
|
912
|
+
});
|
|
901
913
|
// An event can overtake the first read; queue a revalidation when that read is still active.
|
|
902
914
|
if (!store.session.info[event.data.sessionID] && !sync.has(`session:${event.data.sessionID}`))
|
|
903
915
|
return;
|
|
@@ -1050,10 +1062,13 @@ export function createData(config) {
|
|
|
1050
1062
|
return;
|
|
1051
1063
|
const location = event.location;
|
|
1052
1064
|
switch (event.type) {
|
|
1053
|
-
case "
|
|
1054
|
-
result.location.model.invalidate(location);
|
|
1065
|
+
case "provider.updated":
|
|
1055
1066
|
result.location.provider.invalidate(location);
|
|
1056
|
-
refresh(() =>
|
|
1067
|
+
refresh(() => result.location.provider.sync(location));
|
|
1068
|
+
break;
|
|
1069
|
+
case "model.updated":
|
|
1070
|
+
result.location.model.invalidate(location);
|
|
1071
|
+
refresh(() => result.location.model.sync(location));
|
|
1057
1072
|
break;
|
|
1058
1073
|
case "agent.updated":
|
|
1059
1074
|
result.location.agent.invalidate(location);
|
|
@@ -1159,7 +1174,7 @@ export function createData(config) {
|
|
|
1159
1174
|
const vcs = locationResource("vcs", (location) => api().vcs.get({ location }));
|
|
1160
1175
|
const shells = locationResource("shell", async (location) => {
|
|
1161
1176
|
const response = await api().shell.list({ location });
|
|
1162
|
-
const ref = { directory: response.location.directory
|
|
1177
|
+
const ref = { directory: response.location.directory };
|
|
1163
1178
|
return {
|
|
1164
1179
|
location: response.location,
|
|
1165
1180
|
data: Object.fromEntries(response.data.map((info) => [info.id, { ...info, location: ref }])),
|
|
@@ -1228,23 +1243,46 @@ export function createData(config) {
|
|
|
1228
1243
|
},
|
|
1229
1244
|
sync(sessionID) {
|
|
1230
1245
|
return sync.run(`session.pending:${sessionID}`, async () => {
|
|
1231
|
-
const
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1246
|
+
const updates = new Map();
|
|
1247
|
+
pendingUpdates.set(sessionID, updates);
|
|
1248
|
+
try {
|
|
1249
|
+
const snapshot = await api().session.inbox.list({ sessionID });
|
|
1250
|
+
// Events can overtake this HTTP response on a remote connection.
|
|
1251
|
+
// Reconcile them before an older snapshot can resurrect delivered input.
|
|
1252
|
+
const current = new Map(snapshot.map((item) => [item.id, item]));
|
|
1253
|
+
updates.forEach((item, id) => {
|
|
1254
|
+
if (item === undefined)
|
|
1255
|
+
current.delete(id);
|
|
1256
|
+
else if (typeof item === "string") {
|
|
1257
|
+
const existing = current.get(id);
|
|
1258
|
+
if (existing)
|
|
1259
|
+
current.set(id, { ...existing, delivery: item });
|
|
1260
|
+
}
|
|
1261
|
+
else
|
|
1262
|
+
current.set(id, item);
|
|
1263
|
+
});
|
|
1264
|
+
const pending = [...current.values()];
|
|
1265
|
+
// A positive read acknowledges admission even when its SSE echo is delayed.
|
|
1266
|
+
pending.forEach((item) => outbox.delete(item.id));
|
|
1267
|
+
// Compactions also coalesce by Session, not just by the proposed ID.
|
|
1268
|
+
if (pending.some((item) => item.type === "compaction"))
|
|
1269
|
+
store.session.pending[sessionID]
|
|
1270
|
+
?.filter((item) => item.type === "compaction")
|
|
1271
|
+
.forEach((item) => outbox.delete(item.id));
|
|
1272
|
+
// Keep optimistic rows still awaiting their echo: this fetch may
|
|
1273
|
+
// have raced ahead of an in-flight admission the server does not
|
|
1274
|
+
// know about yet.
|
|
1275
|
+
const inflight = (store.session.pending[sessionID] ?? []).filter((item) => outbox.has(item.id));
|
|
1276
|
+
const merged = inflight.length === 0 ? pending : [...pending, ...inflight];
|
|
1277
|
+
batch(() => {
|
|
1278
|
+
setStore("session", "pending", sessionID, reconcile(merged));
|
|
1279
|
+
merged.forEach(materializeInboxMessage);
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
finally {
|
|
1283
|
+
if (pendingUpdates.get(sessionID) === updates)
|
|
1284
|
+
pendingUpdates.delete(sessionID);
|
|
1285
|
+
}
|
|
1248
1286
|
});
|
|
1249
1287
|
},
|
|
1250
1288
|
invalidate(sessionID) {
|
|
@@ -1314,7 +1352,7 @@ export function createData(config) {
|
|
|
1314
1352
|
admitLocal({
|
|
1315
1353
|
id,
|
|
1316
1354
|
sessionID: input.sessionID,
|
|
1317
|
-
|
|
1355
|
+
time: { created: Date.now() },
|
|
1318
1356
|
type: "compaction",
|
|
1319
1357
|
delivery: "steer",
|
|
1320
1358
|
payload: {},
|
|
@@ -1369,7 +1407,7 @@ export function createData(config) {
|
|
|
1369
1407
|
admitLocal({
|
|
1370
1408
|
id,
|
|
1371
1409
|
sessionID: request.sessionID,
|
|
1372
|
-
|
|
1410
|
+
time: { created: Date.now() },
|
|
1373
1411
|
type: "user",
|
|
1374
1412
|
delivery: request.delivery ?? "steer",
|
|
1375
1413
|
// Files and skills stay off the optimistic row: their durable
|
|
@@ -1557,12 +1595,11 @@ export function createData(config) {
|
|
|
1557
1595
|
const key = `session.form:${sessionID}:${sessionID === "global" ? locationKey(ref ?? defaultLocation()) : ""}`;
|
|
1558
1596
|
return sync.run(key, async () => {
|
|
1559
1597
|
if (sessionID === "global") {
|
|
1560
|
-
const response = await api().form.
|
|
1598
|
+
const response = await api().form.list({
|
|
1561
1599
|
location: locationQuery(ref ?? defaultLocation()),
|
|
1562
1600
|
});
|
|
1563
1601
|
const location = {
|
|
1564
1602
|
directory: response.location.directory,
|
|
1565
|
-
workspaceID: response.location.workspaceID,
|
|
1566
1603
|
};
|
|
1567
1604
|
const locationID = locationKey(location);
|
|
1568
1605
|
setStore("session", "form", sessionID, [
|
|
@@ -1571,17 +1608,17 @@ export function createData(config) {
|
|
|
1571
1608
|
]);
|
|
1572
1609
|
return;
|
|
1573
1610
|
}
|
|
1574
|
-
setStore("session", "form", sessionID, await api().form.list({ sessionID }));
|
|
1611
|
+
setStore("session", "form", sessionID, await api().session.form.list({ sessionID }));
|
|
1575
1612
|
});
|
|
1576
1613
|
},
|
|
1577
1614
|
invalidate(sessionID, ref) {
|
|
1578
1615
|
sync.invalidate(`session.form:${sessionID}:${sessionID === "global" ? locationKey(ref ?? defaultLocation()) : ""}`);
|
|
1579
1616
|
},
|
|
1580
1617
|
reply(input, ref) {
|
|
1581
|
-
return settleForm(input, ref, api().form.reply(input, formRequestOptions(input.sessionID, ref)));
|
|
1618
|
+
return settleForm(input, ref, api().session.form.reply(input, formRequestOptions(input.sessionID, ref)));
|
|
1582
1619
|
},
|
|
1583
1620
|
cancel(input, ref) {
|
|
1584
|
-
return settleForm(input, ref, api().form.cancel(input, formRequestOptions(input.sessionID, ref)));
|
|
1621
|
+
return settleForm(input, ref, api().session.form.cancel(input, formRequestOptions(input.sessionID, ref)));
|
|
1585
1622
|
},
|
|
1586
1623
|
},
|
|
1587
1624
|
},
|
|
@@ -1648,7 +1685,7 @@ export function createData(config) {
|
|
|
1648
1685
|
setStore("location", key, {});
|
|
1649
1686
|
setStore("location", key, "info", location);
|
|
1650
1687
|
if (!ref) {
|
|
1651
|
-
setDefaultLocation({ directory: location.directory
|
|
1688
|
+
setDefaultLocation({ directory: location.directory });
|
|
1652
1689
|
}
|
|
1653
1690
|
});
|
|
1654
1691
|
},
|
|
@@ -1691,7 +1728,7 @@ export function createData(config) {
|
|
|
1691
1728
|
agent: locationResource("agent", (location) => api().agent.list({ location })),
|
|
1692
1729
|
command: locationResource("command", (location) => api().command.list({ location })),
|
|
1693
1730
|
config: locationResource("config", async (location) => ({
|
|
1694
|
-
location: { directory: location.directory
|
|
1731
|
+
location: { directory: location.directory },
|
|
1695
1732
|
data: await api().config.get({ location }),
|
|
1696
1733
|
})),
|
|
1697
1734
|
integration: locationResource("integration", (location) => api().integration.list({ location })),
|
package/dist/solid/pty.js
CHANGED
|
@@ -9,8 +9,6 @@ export function createPtyClient(api, options) {
|
|
|
9
9
|
const url = new URL(`/api/pty/${encodeURIComponent(input.ptyID)}/connect`, options.url);
|
|
10
10
|
if (input.location?.directory)
|
|
11
11
|
url.searchParams.set("location[directory]", input.location.directory);
|
|
12
|
-
if (input.location?.workspace)
|
|
13
|
-
url.searchParams.set("location[workspace]", input.location.workspace);
|
|
14
12
|
if (input.cursor !== undefined)
|
|
15
13
|
url.searchParams.set("cursor", String(input.cursor));
|
|
16
14
|
url.searchParams.set("ticket", result.data.ticket);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@opencode/client",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"typecheck": "tsgo --noEmit"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@opencode/schema": "2.0.
|
|
61
|
-
"@opencode/protocol": "2.0.
|
|
60
|
+
"@opencode/schema": "2.0.4",
|
|
61
|
+
"@opencode/protocol": "2.0.4"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"effect": "4.0.0-rc.112",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@effect/platform-node": "4.0.0-rc.112",
|
|
77
|
-
"@opencode/httpapi-codegen": "2.0.
|
|
77
|
+
"@opencode/httpapi-codegen": "2.0.4",
|
|
78
78
|
"@tsconfig/bun": "1.0.9",
|
|
79
79
|
"@types/bun": "1.4.0",
|
|
80
80
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|