@opencode-ai/client 0.0.0-dev-18592 → 0.0.0-dev-18598
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/solid/data.d.ts +6 -1
- package/dist/solid/data.js +98 -25
- package/package.json +4 -4
package/dist/solid/data.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentInfo, CommandInfo, FormCancelInput, FormInfo, FormReplyInput, IntegrationInfo, LocationRef, LocationGetOutput, McpResource, McpServer, ModelInfo, ModelRef, PermissionSavedInfo, PermissionRequest, PermissionReplyInput, Project, ProviderInfo, ReferenceInfo, SessionMessageInfo, SessionInfo, SessionInboxInfo, ShellInfo, SkillInfo, VcsInfo, OpenCodeEvent, OpenCodeClient, WebSearchProvider } from "../promise";
|
|
1
|
+
import type { AgentInfo, CommandInfo, FormCancelInput, FormInfo, FormReplyInput, IntegrationInfo, LocationRef, LocationGetOutput, 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
|
export type CreateDataInput = {
|
|
@@ -62,8 +62,13 @@ export declare function createData(config: CreateDataInput): {
|
|
|
62
62
|
id: string;
|
|
63
63
|
request: Promise<SessionInfo>;
|
|
64
64
|
};
|
|
65
|
+
compact(input: {
|
|
66
|
+
sessionID: string;
|
|
67
|
+
model?: ModelRef;
|
|
68
|
+
}): Promise<SessionInboxCompaction>;
|
|
65
69
|
prompt(input: SessionPromptInput & {
|
|
66
70
|
gate?: Promise<unknown>;
|
|
71
|
+
prepare?: () => Promise<unknown>;
|
|
67
72
|
}): Promise<import("../promise").SessionInboxUser>;
|
|
68
73
|
sync(sessionID: string, options?: {
|
|
69
74
|
children?: boolean;
|
package/dist/solid/data.js
CHANGED
|
@@ -164,12 +164,11 @@ export function createData(config) {
|
|
|
164
164
|
return;
|
|
165
165
|
setStore("session", "pending", sessionID, index, { ...item, delivery });
|
|
166
166
|
}
|
|
167
|
-
// Inbox IDs of optimistic
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
// rollback — not on POST success, which typically precedes the echo.
|
|
167
|
+
// Inbox IDs of optimistic admissions awaiting acknowledgement, so rejection
|
|
168
|
+
// only rolls back unacknowledged rows and a pending re-fetch cannot wipe a
|
|
169
|
+
// row the server does not know about yet. Prompts clear on their durable
|
|
170
|
+
// echo, positive pending read, or rollback; compactions also reconcile the
|
|
171
|
+
// POST's canonical ID.
|
|
173
172
|
const outbox = new Set();
|
|
174
173
|
// Session IDs of optimistic create admissions still awaiting acknowledgement
|
|
175
174
|
// (the session.created echo or the create response itself). A failed create
|
|
@@ -180,11 +179,12 @@ export function createData(config) {
|
|
|
180
179
|
// this so a prompt sent to a still-creating session waits for the session
|
|
181
180
|
// to exist server-side instead of failing with "not found".
|
|
182
181
|
const creating = new Map();
|
|
183
|
-
// Per-session send chain: prompts must be admitted in
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// the next) before sending its own.
|
|
182
|
+
// Per-session send chain: prompts and compactions must be admitted in
|
|
183
|
+
// submission order. Each waits for the previous POST to settle, so one
|
|
184
|
+
// failure does not block the next.
|
|
187
185
|
const sending = new Map();
|
|
186
|
+
const compacting = new Map();
|
|
187
|
+
onCleanup(() => compacting.clear());
|
|
188
188
|
// Register `promise` under `key` until it settles. A later registration
|
|
189
189
|
// replaces an earlier one; settlement only clears its own entry.
|
|
190
190
|
function track(map, key, promise) {
|
|
@@ -195,9 +195,19 @@ export function createData(config) {
|
|
|
195
195
|
};
|
|
196
196
|
void promise.then(settle, settle);
|
|
197
197
|
}
|
|
198
|
+
// Capture creation before settlement clears its entry, so dependent RPCs still see a failed create.
|
|
199
|
+
function sendAdmission(sessionID, send, gate) {
|
|
200
|
+
const created = creating.get(sessionID);
|
|
201
|
+
const previous = sending.get(sessionID);
|
|
202
|
+
const request = Promise.resolve()
|
|
203
|
+
.then(() => Promise.all([gate, created, previous]))
|
|
204
|
+
.then(send);
|
|
205
|
+
track(sending, sessionID, request.catch(() => undefined));
|
|
206
|
+
return request;
|
|
207
|
+
}
|
|
198
208
|
// Upsert an admitted inbox item into pending, input, and (for user and
|
|
199
209
|
// synthetic items) the visible transcript. Used by the inbox.enqueued
|
|
200
|
-
// handler and by optimistic
|
|
210
|
+
// handler and by optimistic admission; the upsert is what reconciles
|
|
201
211
|
// the durable echo with an optimistic placeholder — the durable payload and
|
|
202
212
|
// times replace the client's guess.
|
|
203
213
|
function admitLocal(item) {
|
|
@@ -205,6 +215,8 @@ export function createData(config) {
|
|
|
205
215
|
const pending = store.session.pending[item.sessionID] ?? [];
|
|
206
216
|
const at = pending.findIndex((entry) => entry.id === item.id);
|
|
207
217
|
setStore("session", "pending", item.sessionID, at < 0 ? [...pending, item] : pending.map((entry, index) => (index === at ? item : entry)));
|
|
218
|
+
if (item.type === "compaction")
|
|
219
|
+
return;
|
|
208
220
|
const input = store.session.input[item.sessionID] ?? [];
|
|
209
221
|
if (!input.includes(item.id))
|
|
210
222
|
setStore("session", "input", item.sessionID, [...input, item.id]);
|
|
@@ -536,6 +548,7 @@ export function createData(config) {
|
|
|
536
548
|
draft.push(existing);
|
|
537
549
|
message.reindex(draft, index, position);
|
|
538
550
|
});
|
|
551
|
+
compacting.get(event.data.sessionID)?.observed.add(event.data.inboxID);
|
|
539
552
|
return;
|
|
540
553
|
}
|
|
541
554
|
case "session.inbox.delivery.changed":
|
|
@@ -543,6 +556,7 @@ export function createData(config) {
|
|
|
543
556
|
return;
|
|
544
557
|
case "session.inbox.cancelled": {
|
|
545
558
|
retractLocal(event.data.sessionID, event.data.inboxID);
|
|
559
|
+
compacting.get(event.data.sessionID)?.observed.add(event.data.inboxID);
|
|
546
560
|
return;
|
|
547
561
|
}
|
|
548
562
|
case "session.inbox.enqueued": {
|
|
@@ -553,6 +567,12 @@ export function createData(config) {
|
|
|
553
567
|
timeCreated: event.created,
|
|
554
568
|
...event.data.item,
|
|
555
569
|
});
|
|
570
|
+
if (event.data.item.type === "compaction") {
|
|
571
|
+
const active = compacting.get(event.data.sessionID);
|
|
572
|
+
active?.observed.add(event.data.inboxID);
|
|
573
|
+
if (active && active.id !== event.data.inboxID && outbox.delete(active.id))
|
|
574
|
+
removePending(event.data.sessionID, active.id);
|
|
575
|
+
}
|
|
556
576
|
return;
|
|
557
577
|
}
|
|
558
578
|
case "session.instructions.updated":
|
|
@@ -853,6 +873,8 @@ export function createData(config) {
|
|
|
853
873
|
time: { created: event.created },
|
|
854
874
|
});
|
|
855
875
|
});
|
|
876
|
+
if (event.data.inputID)
|
|
877
|
+
compacting.get(event.data.sessionID)?.observed.add(event.data.inputID);
|
|
856
878
|
return;
|
|
857
879
|
case "session.execution.succeeded":
|
|
858
880
|
case "session.execution.failed":
|
|
@@ -953,6 +975,8 @@ export function createData(config) {
|
|
|
953
975
|
}
|
|
954
976
|
message.append(draft, index, failed);
|
|
955
977
|
});
|
|
978
|
+
if (event.data.inputID)
|
|
979
|
+
compacting.get(event.data.sessionID)?.observed.add(event.data.inputID);
|
|
956
980
|
return;
|
|
957
981
|
case "permission.asked":
|
|
958
982
|
if (store.session.permission[event.data.sessionID]?.some((request) => request.id === event.data.id))
|
|
@@ -1138,10 +1162,17 @@ export function createData(config) {
|
|
|
1138
1162
|
sync(sessionID) {
|
|
1139
1163
|
return sync.run(`session.pending:${sessionID}`, async () => {
|
|
1140
1164
|
const pending = await api().session.inbox.list({ sessionID });
|
|
1165
|
+
// A positive read acknowledges admission even when its SSE echo is delayed.
|
|
1166
|
+
pending.forEach((item) => outbox.delete(item.id));
|
|
1167
|
+
// Compactions also coalesce by Session, not just by the proposed ID.
|
|
1168
|
+
if (pending.some((item) => item.type === "compaction"))
|
|
1169
|
+
store.session.pending[sessionID]
|
|
1170
|
+
?.filter((item) => item.type === "compaction")
|
|
1171
|
+
.forEach((item) => outbox.delete(item.id));
|
|
1141
1172
|
// Keep optimistic rows still awaiting their echo: this fetch may
|
|
1142
1173
|
// have raced ahead of an in-flight admission the server does not
|
|
1143
1174
|
// know about yet.
|
|
1144
|
-
const inflight = (store.session.pending[sessionID] ?? []).filter((item) => outbox.has(item.id)
|
|
1175
|
+
const inflight = (store.session.pending[sessionID] ?? []).filter((item) => outbox.has(item.id));
|
|
1145
1176
|
const merged = inflight.length === 0 ? pending : [...pending, ...inflight];
|
|
1146
1177
|
batch(() => {
|
|
1147
1178
|
setStore("session", "pending", sessionID, reconcile(merged));
|
|
@@ -1205,13 +1236,62 @@ export function createData(config) {
|
|
|
1205
1236
|
track(creating, id, request);
|
|
1206
1237
|
return { id, request };
|
|
1207
1238
|
},
|
|
1239
|
+
compact(input) {
|
|
1240
|
+
const active = compacting.get(input.sessionID);
|
|
1241
|
+
if (active)
|
|
1242
|
+
return active.request;
|
|
1243
|
+
// A known pending control ID may be consumed while setup waits. Propose
|
|
1244
|
+
// a fresh ID and let the server coalesce, without duplicating its row.
|
|
1245
|
+
const id = SessionMessage.ID.create();
|
|
1246
|
+
if (!store.session.pending[input.sessionID]?.some((item) => item.type === "compaction")) {
|
|
1247
|
+
outbox.add(id);
|
|
1248
|
+
admitLocal({
|
|
1249
|
+
id,
|
|
1250
|
+
sessionID: input.sessionID,
|
|
1251
|
+
timeCreated: Date.now(),
|
|
1252
|
+
type: "compaction",
|
|
1253
|
+
delivery: "steer",
|
|
1254
|
+
payload: {},
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
// Compaction admission can coalesce onto a different ID. Retire the
|
|
1258
|
+
// speculative row on an echo, and remember consumed IDs until the POST
|
|
1259
|
+
// settles so its older response cannot resurrect a queued row.
|
|
1260
|
+
const observed = new Set();
|
|
1261
|
+
const request = sendAdmission(input.sessionID, async () => {
|
|
1262
|
+
if (input.model)
|
|
1263
|
+
await api().session.switchModel({ sessionID: input.sessionID, model: input.model });
|
|
1264
|
+
return api().session.compact({ sessionID: input.sessionID, id });
|
|
1265
|
+
})
|
|
1266
|
+
.then((item) => {
|
|
1267
|
+
batch(() => {
|
|
1268
|
+
outbox.delete(id);
|
|
1269
|
+
if (item.id !== id)
|
|
1270
|
+
removePending(input.sessionID, id);
|
|
1271
|
+
if (!observed.has(item.id) && !messageIndex.get(input.sessionID)?.has(item.id))
|
|
1272
|
+
admitLocal(item);
|
|
1273
|
+
});
|
|
1274
|
+
return item;
|
|
1275
|
+
})
|
|
1276
|
+
.catch((error) => {
|
|
1277
|
+
if (outbox.delete(id))
|
|
1278
|
+
removePending(input.sessionID, id);
|
|
1279
|
+
throw error;
|
|
1280
|
+
})
|
|
1281
|
+
.finally(() => {
|
|
1282
|
+
if (compacting.get(input.sessionID)?.request === request)
|
|
1283
|
+
compacting.delete(input.sessionID);
|
|
1284
|
+
});
|
|
1285
|
+
compacting.set(input.sessionID, { id, observed, request });
|
|
1286
|
+
return request;
|
|
1287
|
+
},
|
|
1208
1288
|
// Optimistic prompt admission: render the prompt immediately under a
|
|
1209
1289
|
// client-minted ID, send it, and let the durable inbox.enqueued echo
|
|
1210
1290
|
// upsert that same ID with the server's payload. Server admission is
|
|
1211
1291
|
// idempotent per ID, so retrying with the identical payload cannot
|
|
1212
1292
|
// double-admit.
|
|
1213
1293
|
prompt(input) {
|
|
1214
|
-
const { gate, ...request } = input;
|
|
1294
|
+
const { gate, prepare, ...request } = input;
|
|
1215
1295
|
const id = request.id ?? SessionMessage.ID.create();
|
|
1216
1296
|
// A retry may reuse an ID that is already rendered — and possibly
|
|
1217
1297
|
// already durable. Admit optimistically only for new IDs so a failed
|
|
@@ -1236,18 +1316,11 @@ export function createData(config) {
|
|
|
1236
1316
|
},
|
|
1237
1317
|
});
|
|
1238
1318
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
const previous = sending.get(request.sessionID);
|
|
1245
|
-
const send = Promise.resolve()
|
|
1246
|
-
.then(() => Promise.all([gate, creating.get(request.sessionID), previous]))
|
|
1247
|
-
.then(() => api().session.prompt({ ...request, id }));
|
|
1248
|
-
track(sending, request.sessionID, send.then(() => undefined, () => undefined));
|
|
1249
|
-
return send.catch((error) => {
|
|
1250
|
-
// Roll back only rows this call admitted and the echo has not
|
|
1319
|
+
return sendAdmission(request.sessionID, async () => {
|
|
1320
|
+
await prepare?.();
|
|
1321
|
+
return api().session.prompt({ ...request, id });
|
|
1322
|
+
}, gate).catch((error) => {
|
|
1323
|
+
// Roll back only rows this call admitted and the server has not
|
|
1251
1324
|
// acknowledged: anything else is server state.
|
|
1252
1325
|
if (fresh && outbox.delete(id))
|
|
1253
1326
|
retractLocal(request.sessionID, id);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@opencode-ai/client",
|
|
4
|
-
"version": "0.0.0-dev-
|
|
4
|
+
"version": "0.0.0-dev-18598",
|
|
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-ai/schema": "0.0.0-dev-
|
|
61
|
-
"@opencode-ai/protocol": "0.0.0-dev-
|
|
60
|
+
"@opencode-ai/schema": "0.0.0-dev-18598",
|
|
61
|
+
"@opencode-ai/protocol": "0.0.0-dev-18598"
|
|
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-ai/httpapi-codegen": "0.0.0-dev-
|
|
77
|
+
"@opencode-ai/httpapi-codegen": "0.0.0-dev-18598",
|
|
78
78
|
"@tsconfig/bun": "1.0.9",
|
|
79
79
|
"@types/bun": "1.3.13",
|
|
80
80
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|