@vinaverse/client 0.1.0-alpha.11 → 0.1.0-alpha.12

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/README.md CHANGED
@@ -183,7 +183,9 @@ against the session it was given in; a reply restored from the device or a resum
183
183
  `{ screen: 'other' }`, no entity); revisions only grow across a conversation's sessions (`placeRevision` on the
184
184
  session request), a `context_conflict` (409, with `current_revision`) sends the newest place once more above it, only while the session
185
185
  and the person are the same, then says `moved`,
186
- and a telling lands only on the session it was told to. `onEvent` also says `message_sent`, `reply_completed` and
186
+ and a telling lands only on the session it was told to. One update is in flight per conversation: a place told meanwhile
187
+ (`show` and `tell` in one move, rapid moves) waits, only the newest, and goes next; a refusal that is no conflict is said
188
+ through `onWarning` (`context_refused`) in development builds. `onEvent` also says `message_sent`, `reply_completed` and
187
189
  `card_shown` (never the words), its `key` is a salted SHA-256 hash of the conversation's key (`hashKey`), never the person's id: stable per install
188
190
  (the salt is kept at `<persistenceKey>:salt` in your storage, a new one after `erase()`), and
189
191
  `session_opened` says where the chat was opened from (`entryPoint`: `launcher`, `ask`, `deep_link`, or your own).
@@ -2590,6 +2590,7 @@ var samePlace = (a, b) => a?.screen === b?.screen && (a?.entity?.type ?? null) =
2590
2590
  function usable(session, insecure) {
2591
2591
  return typeof session.id === "string" && session.id !== "" && (insecure.realtime === true || /^wss:\/\//i.test(session.realtime_url ?? "")) && (insecure.turns === true || /^https:\/\//i.test(session.turns_url ?? "")) && Boolean(session.client_secret);
2592
2592
  }
2593
+ var devBuild = () => globalThis.__DEV__ === true;
2593
2594
  function warnInsecure(insecure, warn) {
2594
2595
  if (!(insecure.realtime || insecure.turns)) return;
2595
2596
  if (globalThis.__DEV__ === true) return;
@@ -2908,6 +2909,8 @@ function createAgentRuntime(options) {
2908
2909
  place: null,
2909
2910
  told: null,
2910
2911
  revision: 0,
2912
+ telling: false,
2913
+ queued: false,
2911
2914
  entryPoint: null,
2912
2915
  expiresAt: null,
2913
2916
  shown: 0,
@@ -2942,26 +2945,55 @@ function createAgentRuntime(options) {
2942
2945
  update(entry, { moved });
2943
2946
  const updateContext = backend.updateContext?.bind(backend);
2944
2947
  if (!moved || !updateContext || sessionId === null) return;
2948
+ if (entry.telling) {
2949
+ entry.queued = true;
2950
+ return;
2951
+ }
2952
+ entry.telling = true;
2945
2953
  const asked = generation;
2946
2954
  const current2 = () => asked === generation && live.get(entry.key) === entry && entry.state.sessionId === sessionId;
2955
+ const settle = () => {
2956
+ entry.telling = false;
2957
+ if (!entry.queued) return;
2958
+ entry.queued = false;
2959
+ if (asked === generation && live.get(entry.key) === entry)
2960
+ tell(entry, {});
2961
+ };
2947
2962
  const send = (again) => {
2948
2963
  const place = entry.place;
2949
2964
  const revision = ++entry.revision;
2950
- updateContext(sessionId, place ?? CLEARED, revision).then(
2965
+ let told;
2966
+ try {
2967
+ told = updateContext(sessionId, place ?? CLEARED, revision);
2968
+ } catch (error) {
2969
+ told = Promise.reject(error);
2970
+ }
2971
+ told.then(
2951
2972
  () => {
2952
- if (!current2() || entry.revision !== revision) return;
2953
- entry.told = place;
2954
- update(entry, { moved: !samePlace(entry.told, entry.place) });
2955
- emit({ type: "context_updated", key: entry.key, sessionId });
2973
+ if (current2() && entry.revision === revision) {
2974
+ entry.told = place;
2975
+ update(entry, { moved: !samePlace(entry.told, entry.place) });
2976
+ emit({ type: "context_updated", key: entry.key, sessionId });
2977
+ }
2978
+ settle();
2956
2979
  },
2957
2980
  (error) => {
2958
2981
  const theirs = field(error, "currentRevision");
2959
2982
  const conflict = field(error, "outcome") === "context_conflict" || field(error, "reason") === "conflict";
2960
- if (!conflict || again || !current2() || entry.revision !== revision)
2983
+ if (conflict && !again && current2() && entry.revision === revision) {
2984
+ if (typeof theirs === "number" && theirs >= entry.revision)
2985
+ entry.revision = theirs;
2986
+ send(true);
2961
2987
  return;
2962
- if (typeof theirs === "number" && theirs >= entry.revision)
2963
- entry.revision = theirs;
2964
- send(true);
2988
+ }
2989
+ if (!conflict && devBuild()) {
2990
+ const status2 = field(error, "status");
2991
+ warn(
2992
+ `@vinaverse/client/agent: updateContext was refused${typeof status2 === "number" ? ` (${status2})` : ""}: ${error instanceof Error ? error.message : String(error)}. The new place is offered as a question instead.`,
2993
+ { code: "context_refused" }
2994
+ );
2995
+ }
2996
+ settle();
2965
2997
  }
2966
2998
  );
2967
2999
  };