@opencode-ai/client 0.0.0-dev-19045 → 0.0.0-dev-19048

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.
@@ -19,6 +19,8 @@ export type CreateDataInput = {
19
19
  readonly connection?: {
20
20
  readonly status: () => "connected" | "connecting" | "reconnecting";
21
21
  };
22
+ /** Receives failed event-driven reads. Explicit reads still reject to their caller. */
23
+ readonly onError?: (error: unknown) => void;
22
24
  };
23
25
  export type FormWithLocation = FormInfo & {
24
26
  readonly location?: LocationRef;
@@ -85,6 +85,19 @@ function createSync() {
85
85
  }
86
86
  export function createData(config) {
87
87
  const api = config.api;
88
+ let disposed = false;
89
+ onCleanup(() => (disposed = true));
90
+ function refresh(load) {
91
+ if (disposed || (config.connection && config.connection.status() !== "connected"))
92
+ return;
93
+ void load().catch((error) => {
94
+ if (disposed || (config.connection && config.connection.status() !== "connected"))
95
+ return;
96
+ if (config.onError)
97
+ return config.onError(error);
98
+ console.error("Failed to refresh client data", error);
99
+ });
100
+ }
88
101
  const [store, setStore] = createStore({
89
102
  session: {
90
103
  info: {},
@@ -420,7 +433,7 @@ export function createData(config) {
420
433
  case "server.connected": {
421
434
  const updates = new Map();
422
435
  activeUpdates = updates;
423
- void api()
436
+ refresh(() => api()
424
437
  .session.active()
425
438
  .then((active) => {
426
439
  if (activeUpdates !== updates)
@@ -438,16 +451,15 @@ export function createData(config) {
438
451
  .catch(() => {
439
452
  if (activeUpdates === updates)
440
453
  activeUpdates = undefined;
441
- });
442
- void api()
454
+ }));
455
+ refresh(() => api()
443
456
  .location.get({ location: locationQuery(defaultLocation()) })
444
457
  .then((location) => {
445
458
  const key = locationKey(location);
446
459
  setStore("location", key, { info: location });
447
- })
448
- .catch((error) => console.error("Failed to preload location", error));
449
- void result.location.vcs.sync().catch((error) => console.error("Failed to preload VCS info", error));
450
- void result.project.sync().catch((error) => console.error("Failed to preload projects", error));
460
+ }));
461
+ refresh(() => result.location.vcs.sync());
462
+ refresh(() => result.project.sync());
451
463
  return;
452
464
  }
453
465
  case "project.updated":
@@ -456,7 +468,7 @@ export function createData(config) {
456
468
  case "session.created":
457
469
  sessionOutbox.delete(event.data.sessionID);
458
470
  result.session.invalidate(event.data.sessionID);
459
- void result.session.sync(event.data.sessionID);
471
+ refresh(() => result.session.sync(event.data.sessionID));
460
472
  // Band-aid: a newly created session starts empty, so live events can be its source of truth.
461
473
  // Fetching pending inputs and projected messages separately lets promotion move an input between snapshots,
462
474
  // causing both requests to miss it and overwrite event-built state. Skip those racy initial reads until
@@ -498,7 +510,7 @@ export function createData(config) {
498
510
  model: event.data.model,
499
511
  time: { created: event.created },
500
512
  });
501
- void api()
513
+ refresh(() => api()
502
514
  .session.message({ sessionID: event.data.sessionID, messageID: messageIDFromEvent(event.id) })
503
515
  .then((item) => {
504
516
  message.update(event.data.sessionID, (draft, index) => {
@@ -507,17 +519,18 @@ export function createData(config) {
507
519
  return message.append(draft, index, item);
508
520
  draft[position] = item;
509
521
  });
510
- })
511
- .catch((error) => console.error("Failed to load projected model switch message", error));
522
+ }));
512
523
  return;
513
524
  case "session.renamed": {
514
525
  // Preserve the live title when it races the session's initial read.
515
- const family = sync.pending(`session.family:${event.data.sessionID}`)
516
- ? result.session.sync(event.data.sessionID, { children: true })
517
- : Promise.resolve();
518
- void Promise.all([result.session.sync(event.data.sessionID), family]).then(() => {
519
- if (store.session.info[event.data.sessionID])
520
- setStore("session", "info", event.data.sessionID, "title", event.data.title);
526
+ refresh(() => {
527
+ const family = sync.pending(`session.family:${event.data.sessionID}`)
528
+ ? result.session.sync(event.data.sessionID, { children: true })
529
+ : Promise.resolve();
530
+ return Promise.all([result.session.sync(event.data.sessionID), family]).then(() => {
531
+ if (store.session.info[event.data.sessionID])
532
+ setStore("session", "info", event.data.sessionID, "title", event.data.title);
533
+ });
521
534
  });
522
535
  return;
523
536
  }
@@ -553,7 +566,7 @@ export function createData(config) {
553
566
  if (info.location.workspaceID)
554
567
  continue;
555
568
  result.session.invalidate(sessionID);
556
- void result.session.sync(sessionID);
569
+ refresh(() => result.session.sync(sessionID));
557
570
  continue;
558
571
  }
559
572
  const adopted = Worktree.adopt({
@@ -666,7 +679,7 @@ export function createData(config) {
666
679
  if (!sync.pending(`session.message:${event.data.sessionID}`))
667
680
  return;
668
681
  result.session.message.invalidate(event.data.sessionID);
669
- void result.session.message.sync(event.data.sessionID);
682
+ refresh(() => result.session.message.sync(event.data.sessionID));
670
683
  return;
671
684
  }
672
685
  case "session.step.started":
@@ -880,13 +893,13 @@ export function createData(config) {
880
893
  if (!store.session.info[event.data.sessionID] && !sync.has(`session:${event.data.sessionID}`))
881
894
  return;
882
895
  result.session.invalidate(event.data.sessionID);
883
- void result.session.sync(event.data.sessionID);
896
+ refresh(() => result.session.sync(event.data.sessionID));
884
897
  return;
885
898
  case "session.viewed":
886
899
  if (!store.session.info[event.data.sessionID] && !sync.has(`session:${event.data.sessionID}`))
887
900
  return;
888
901
  result.session.invalidate(event.data.sessionID);
889
- void result.session.sync(event.data.sessionID);
902
+ refresh(() => result.session.sync(event.data.sessionID));
890
903
  return;
891
904
  case "session.revert.staged":
892
905
  if (store.session.info[event.data.sessionID])
@@ -994,7 +1007,7 @@ export function createData(config) {
994
1007
  const location = { directory: ref[0], workspaceID: ref[1] ?? undefined };
995
1008
  if (event.type === "credential.updated") {
996
1009
  result.location.integration.invalidate(location);
997
- void result.location.integration.sync(location);
1010
+ refresh(() => result.location.integration.sync(location));
998
1011
  return;
999
1012
  }
1000
1013
  setStore("location", key, (data) => ({
@@ -1012,7 +1025,7 @@ export function createData(config) {
1012
1025
  }));
1013
1026
  result.location.model.invalidate(location);
1014
1027
  result.location.provider.invalidate(location);
1015
- void Promise.all([result.location.model.sync(location), result.location.provider.sync(location)]);
1028
+ refresh(() => Promise.all([result.location.model.sync(location), result.location.provider.sync(location)]));
1016
1029
  });
1017
1030
  return;
1018
1031
  }
@@ -1023,19 +1036,19 @@ export function createData(config) {
1023
1036
  case "catalog.updated":
1024
1037
  result.location.model.invalidate(location);
1025
1038
  result.location.provider.invalidate(location);
1026
- void Promise.all([result.location.model.sync(location), result.location.provider.sync(location)]);
1039
+ refresh(() => Promise.all([result.location.model.sync(location), result.location.provider.sync(location)]));
1027
1040
  break;
1028
1041
  case "agent.updated":
1029
1042
  result.location.agent.invalidate(location);
1030
- void result.location.agent.sync(location);
1043
+ refresh(() => result.location.agent.sync(location));
1031
1044
  break;
1032
1045
  case "command.updated":
1033
1046
  result.location.command.invalidate(location);
1034
- void result.location.command.sync(location);
1047
+ refresh(() => result.location.command.sync(location));
1035
1048
  break;
1036
1049
  case "skill.updated":
1037
1050
  result.location.skill.invalidate(location);
1038
- void result.location.skill.sync(location);
1051
+ refresh(() => result.location.skill.sync(location));
1039
1052
  break;
1040
1053
  case "vcs.branch.updated":
1041
1054
  setStore("location", locationKey(location), (data) => ({
@@ -1071,31 +1084,31 @@ export function createData(config) {
1071
1084
  break;
1072
1085
  case "reference.updated":
1073
1086
  result.location.reference.invalidate(location);
1074
- void result.location.reference.sync(location);
1087
+ refresh(() => result.location.reference.sync(location));
1075
1088
  break;
1076
1089
  case "integration.updated":
1077
1090
  result.location.integration.invalidate(location);
1078
1091
  result.location.model.invalidate(location);
1079
1092
  result.location.provider.invalidate(location);
1080
- void Promise.all([
1093
+ refresh(() => Promise.all([
1081
1094
  result.location.integration.sync(location),
1082
1095
  result.location.model.sync(location),
1083
1096
  result.location.provider.sync(location),
1084
- ]);
1097
+ ]));
1085
1098
  break;
1086
1099
  case "config.updated":
1087
1100
  case "websearch.updated":
1088
- void result.location.websearch.refresh(location);
1101
+ refresh(() => result.location.websearch.refresh(location));
1089
1102
  break;
1090
1103
  // Authenticating an MCP integration reconnects its server, which emits mcp.status.changed,
1091
1104
  // so the mcp list syncs here rather than off integration.updated.
1092
1105
  case "mcp.status.changed":
1093
1106
  result.location.mcp.server.invalidate(location);
1094
- void result.location.mcp.server.sync(location);
1107
+ refresh(() => result.location.mcp.server.sync(location));
1095
1108
  break;
1096
1109
  case "mcp.resources.changed":
1097
1110
  result.location.mcp.resource.invalidate(location);
1098
- void result.location.mcp.resource.sync(location);
1111
+ refresh(() => result.location.mcp.resource.sync(location));
1099
1112
  break;
1100
1113
  }
1101
1114
  }
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-19045",
4
+ "version": "0.0.0-dev-19048",
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-19045",
61
- "@opencode-ai/protocol": "0.0.0-dev-19045"
60
+ "@opencode-ai/schema": "0.0.0-dev-19048",
61
+ "@opencode-ai/protocol": "0.0.0-dev-19048"
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-19045",
77
+ "@opencode-ai/httpapi-codegen": "0.0.0-dev-19048",
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",