@opencode-ai/client 0.0.0-dev-17829 → 0.0.0-dev-17843
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 +15 -2
- package/dist/solid/data.js +98 -12
- package/package.json +4 -4
package/dist/solid/data.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentInfo, CommandInfo, FormInfo, IntegrationInfo, LocationRef, LocationGetOutput, McpResource, McpServer, ModelInfo, PermissionSavedInfo, PermissionRequest, PermissionReplyInput, Project, ProviderInfo, ReferenceInfo, SessionMessageInfo, SessionInfo, SessionInboxInfo, ShellInfo, SkillInfo, VcsInfo, OpenCodeEvent, OpenCodeClient, WebSearchProvider } from "../promise";
|
|
1
|
+
import type { AgentInfo, CommandInfo, FormInfo, IntegrationInfo, LocationRef, LocationGetOutput, McpResource, McpServer, ModelInfo, ModelRef, PermissionSavedInfo, PermissionRequest, PermissionReplyInput, Project, ProviderInfo, ReferenceInfo, SessionMessageInfo, SessionInfo, SessionInboxInfo, 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 = {
|
|
@@ -50,7 +50,20 @@ export declare function createData(config: CreateDataInput): {
|
|
|
50
50
|
sync(sessionID: string): Promise<void>;
|
|
51
51
|
invalidate(sessionID: string): void;
|
|
52
52
|
};
|
|
53
|
-
|
|
53
|
+
create(input: {
|
|
54
|
+
id?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
agent?: string;
|
|
57
|
+
model?: ModelRef;
|
|
58
|
+
location?: LocationRef;
|
|
59
|
+
projectID?: string;
|
|
60
|
+
}): {
|
|
61
|
+
id: string;
|
|
62
|
+
request: Promise<SessionInfo>;
|
|
63
|
+
};
|
|
64
|
+
prompt(input: SessionPromptInput & {
|
|
65
|
+
gate?: Promise<unknown>;
|
|
66
|
+
}): Promise<import("../promise").SessionInboxUser>;
|
|
54
67
|
sync(sessionID: string, options?: {
|
|
55
68
|
children?: boolean;
|
|
56
69
|
}): Promise<void>;
|
package/dist/solid/data.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// merges, live/history overlays, or other race machinery here—last write wins.
|
|
4
4
|
// Reconnect invalidates cached reads; active UI owners decide what to sync again.
|
|
5
5
|
import { Worktree } from "@opencode-ai/schema/worktree";
|
|
6
|
+
import { SessionID } from "@opencode-ai/schema/session-id";
|
|
6
7
|
import { SessionMessage } from "@opencode-ai/schema/session-message";
|
|
7
8
|
import { isPermissionNotFoundError } from "../promise";
|
|
8
9
|
import { createStore, produce, reconcile } from "solid-js/store";
|
|
@@ -105,6 +106,30 @@ export function createData(config) {
|
|
|
105
106
|
// server does not know about yet. Entries clear on the enqueued echo or on
|
|
106
107
|
// rollback — not on POST success, which typically precedes the echo.
|
|
107
108
|
const outbox = new Set();
|
|
109
|
+
// Session IDs of optimistic create admissions still awaiting acknowledgement
|
|
110
|
+
// (the session.created echo or the create response itself). A failed create
|
|
111
|
+
// only rolls back a session the server never acknowledged. Unlike
|
|
112
|
+
// `creating`, this clears on the echo rather than request settlement.
|
|
113
|
+
const sessionOutbox = new Set();
|
|
114
|
+
// In-flight optimistic creates by session ID. prompt() gates its POST on
|
|
115
|
+
// this so a prompt sent to a still-creating session waits for the session
|
|
116
|
+
// to exist server-side instead of failing with "not found".
|
|
117
|
+
const creating = new Map();
|
|
118
|
+
// Per-session send chain: prompts must be admitted in submission order,
|
|
119
|
+
// and HTTP gives no ordering across concurrent POSTs. Each prompt waits
|
|
120
|
+
// for the previous prompt's POST (settled, so one failure does not block
|
|
121
|
+
// the next) before sending its own.
|
|
122
|
+
const sending = new Map();
|
|
123
|
+
// Register `promise` under `key` until it settles. A later registration
|
|
124
|
+
// replaces an earlier one; settlement only clears its own entry.
|
|
125
|
+
function track(map, key, promise) {
|
|
126
|
+
map.set(key, promise);
|
|
127
|
+
const settle = () => {
|
|
128
|
+
if (map.get(key) === promise)
|
|
129
|
+
map.delete(key);
|
|
130
|
+
};
|
|
131
|
+
void promise.then(settle, settle);
|
|
132
|
+
}
|
|
108
133
|
// Upsert an admitted inbox item into pending, input, and (for user and
|
|
109
134
|
// synthetic items) the visible transcript. Used by the inbox.enqueued
|
|
110
135
|
// handler and by optimistic prompt admission; the upsert is what reconciles
|
|
@@ -251,6 +276,7 @@ export function createData(config) {
|
|
|
251
276
|
store.session.pending[sessionID]?.forEach((item) => outbox.delete(item.id));
|
|
252
277
|
messageIndex.delete(sessionID);
|
|
253
278
|
sync.invalidate(`session:${sessionID}`);
|
|
279
|
+
sync.invalidate(`session.family:${sessionID}`);
|
|
254
280
|
sync.invalidate(`session.pending:${sessionID}`);
|
|
255
281
|
sync.invalidate(`session.message:${sessionID}`);
|
|
256
282
|
sync.invalidate(`session.permission:${sessionID}`);
|
|
@@ -294,6 +320,7 @@ export function createData(config) {
|
|
|
294
320
|
void result.project.sync().catch((error) => console.error("Failed to preload projects", error));
|
|
295
321
|
return;
|
|
296
322
|
case "session.created":
|
|
323
|
+
sessionOutbox.delete(event.data.sessionID);
|
|
297
324
|
result.session.invalidate(event.data.sessionID);
|
|
298
325
|
void result.session.sync(event.data.sessionID);
|
|
299
326
|
// Band-aid: a newly created session starts empty, so live events can be its source of truth.
|
|
@@ -963,44 +990,103 @@ export function createData(config) {
|
|
|
963
990
|
sync.invalidate(`session.pending:${sessionID}`);
|
|
964
991
|
},
|
|
965
992
|
},
|
|
993
|
+
// Optimistic session creation: admit a local record under a
|
|
994
|
+
// client-minted ID so a session view can mount immediately, then create
|
|
995
|
+
// the session on the server. The session.created echo re-syncs the
|
|
996
|
+
// record by ID, so the durable payload replaces the client's guess.
|
|
997
|
+
// Returns the ID synchronously along with the in-flight request:
|
|
998
|
+
// callers gate session-dependent sends on the request (prompt() gates
|
|
999
|
+
// itself on any in-flight create of its session automatically).
|
|
1000
|
+
create(input) {
|
|
1001
|
+
const { projectID, ...payload } = input;
|
|
1002
|
+
const id = payload.id ?? SessionID.create();
|
|
1003
|
+
const location = payload.location ?? defaultLocation();
|
|
1004
|
+
const fresh = !store.session.info[id];
|
|
1005
|
+
if (fresh) {
|
|
1006
|
+
const now = Date.now();
|
|
1007
|
+
sessionOutbox.add(id);
|
|
1008
|
+
result.session.remember({
|
|
1009
|
+
id,
|
|
1010
|
+
projectID: projectID ?? store.location[locationKey(location)]?.info?.project.id ?? "",
|
|
1011
|
+
agent: payload.agent,
|
|
1012
|
+
model: payload.model,
|
|
1013
|
+
cost: 0,
|
|
1014
|
+
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
1015
|
+
time: { created: now, updated: now },
|
|
1016
|
+
title: payload.title,
|
|
1017
|
+
location,
|
|
1018
|
+
});
|
|
1019
|
+
// A mounted optimistic session must not fetch its empty collections
|
|
1020
|
+
// before creation settles. The session.created echo re-syncs info.
|
|
1021
|
+
sync.complete(`session.family:${id}`);
|
|
1022
|
+
sync.complete(`session.pending:${id}`);
|
|
1023
|
+
sync.complete(`session.message:${id}`);
|
|
1024
|
+
}
|
|
1025
|
+
// Wrapped so even a synchronous client failure reaches the rollback.
|
|
1026
|
+
const request = Promise.resolve()
|
|
1027
|
+
.then(() => api().session.create({ ...payload, id, location }))
|
|
1028
|
+
.then((info) => {
|
|
1029
|
+
sessionOutbox.delete(id);
|
|
1030
|
+
result.session.remember(info);
|
|
1031
|
+
return info;
|
|
1032
|
+
})
|
|
1033
|
+
.catch((error) => {
|
|
1034
|
+
// Roll back only a record this call admitted and neither the echo
|
|
1035
|
+
// nor the response has acknowledged: anything else is server state.
|
|
1036
|
+
if (fresh && sessionOutbox.delete(id))
|
|
1037
|
+
removeSession(id);
|
|
1038
|
+
throw error;
|
|
1039
|
+
});
|
|
1040
|
+
if (fresh)
|
|
1041
|
+
track(creating, id, request);
|
|
1042
|
+
return { id, request };
|
|
1043
|
+
},
|
|
966
1044
|
// Optimistic prompt admission: render the prompt immediately under a
|
|
967
1045
|
// client-minted ID, send it, and let the durable inbox.enqueued echo
|
|
968
1046
|
// upsert that same ID with the server's payload. Server admission is
|
|
969
1047
|
// idempotent per ID, so retrying with the identical payload cannot
|
|
970
1048
|
// double-admit.
|
|
971
1049
|
prompt(input) {
|
|
972
|
-
const
|
|
1050
|
+
const { gate, ...request } = input;
|
|
1051
|
+
const id = request.id ?? SessionMessage.ID.create();
|
|
973
1052
|
// A retry may reuse an ID that is already rendered — and possibly
|
|
974
1053
|
// already durable. Admit optimistically only for new IDs so a failed
|
|
975
1054
|
// retry cannot roll back acknowledged state.
|
|
976
|
-
const fresh = !messageIndex.get(
|
|
977
|
-
!store.session.pending[
|
|
1055
|
+
const fresh = !messageIndex.get(request.sessionID)?.has(id) &&
|
|
1056
|
+
!store.session.pending[request.sessionID]?.some((item) => item.id === id);
|
|
978
1057
|
if (fresh) {
|
|
979
1058
|
outbox.add(id);
|
|
980
1059
|
admitLocal({
|
|
981
1060
|
id,
|
|
982
|
-
sessionID:
|
|
1061
|
+
sessionID: request.sessionID,
|
|
983
1062
|
timeCreated: Date.now(),
|
|
984
1063
|
type: "user",
|
|
985
|
-
delivery:
|
|
1064
|
+
delivery: request.delivery ?? "steer",
|
|
986
1065
|
// Files and skills stay off the optimistic row: their durable
|
|
987
1066
|
// forms are server-loaded (content, mime, resolution), so they
|
|
988
1067
|
// fill in when the echo upserts the row.
|
|
989
1068
|
payload: {
|
|
990
|
-
text:
|
|
991
|
-
agents:
|
|
992
|
-
metadata:
|
|
1069
|
+
text: request.text,
|
|
1070
|
+
agents: request.agents?.map((agent) => ({ ...agent })),
|
|
1071
|
+
metadata: request.metadata,
|
|
993
1072
|
},
|
|
994
1073
|
});
|
|
995
1074
|
}
|
|
996
1075
|
// Wrapped so even a synchronous client failure reaches the rollback.
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1076
|
+
// The POST additionally waits for the caller's gate, for any
|
|
1077
|
+
// in-flight optimistic create of this session, and for the previous
|
|
1078
|
+
// prompt's POST: the row renders now, the send happens once the
|
|
1079
|
+
// session exists server-side and earlier prompts are admitted.
|
|
1080
|
+
const previous = sending.get(request.sessionID);
|
|
1081
|
+
const send = Promise.resolve()
|
|
1082
|
+
.then(() => Promise.all([gate, creating.get(request.sessionID), previous]))
|
|
1083
|
+
.then(() => api().session.prompt({ ...request, id }));
|
|
1084
|
+
track(sending, request.sessionID, send.then(() => undefined, () => undefined));
|
|
1085
|
+
return send.catch((error) => {
|
|
1000
1086
|
// Roll back only rows this call admitted and the echo has not
|
|
1001
1087
|
// acknowledged: anything else is server state.
|
|
1002
1088
|
if (fresh && outbox.delete(id))
|
|
1003
|
-
retractLocal(
|
|
1089
|
+
retractLocal(request.sessionID, id);
|
|
1004
1090
|
throw error;
|
|
1005
1091
|
});
|
|
1006
1092
|
},
|
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-17843",
|
|
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-17843",
|
|
61
|
+
"@opencode-ai/protocol": "0.0.0-dev-17843"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"effect": "4.0.0-rc.110",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@effect/platform-node": "4.0.0-rc.110",
|
|
77
|
-
"@opencode-ai/httpapi-codegen": "0.0.0-dev-
|
|
77
|
+
"@opencode-ai/httpapi-codegen": "0.0.0-dev-17843",
|
|
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",
|