@burdenoff/fe-libs 2026.1019.1 → 2026.1019.3

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.
@@ -5,8 +5,9 @@ import { readSandboxWriteCapabilitiesOnce as n } from "../api.js";
5
5
  import { writeProposalErrorLine as r } from "../writeProposalErrorCopy.js";
6
6
  import { getOrganizationId as i, getWorkspaceId as a } from "../context.js";
7
7
  import { redactWriteProposalText as o, writeProposalConfirmState as s, writeProposalSensitiveTexts as c } from "./writeProposalPresentation.js";
8
- import { useAssistantPreview as l } from "../preview/context.js";
9
- import { currentPreviewOwner as u, sameOwner as d } from "../preview/surfaceState.js";
8
+ import { statusText as l } from "./AssistantWriteProposalCard.js";
9
+ import { useAssistantPreview as u } from "../preview/context.js";
10
+ import { currentPreviewOwner as d, sameOwner as ee } from "../preview/surfaceState.js";
10
11
  import { deliverPreviewEvent as f } from "../preview/useAssistantPreviewBridge.js";
11
12
  import { writeActionResultEvents as p } from "../preview/writeActionResults.js";
12
13
  import { WRITE_PROPOSAL_SANDBOX_MODE as m, createSandboxWriteProposalClient as h } from "../writeProposals.js";
@@ -21,8 +22,12 @@ var D = new Set([
21
22
  "declined",
22
23
  "superseded",
23
24
  "expired"
24
- ]), O = (e) => `${e.sandboxId}\u0000${e.sessionId}`;
25
- function k(e) {
25
+ ]);
26
+ function O(e) {
27
+ return D.has(e) && e !== "outcome_unknown";
28
+ }
29
+ var k = (e) => `${e.sandboxId}\u0000${e.sessionId}`;
30
+ function A(e) {
26
31
  return {
27
32
  accessToken: e.getAccessToken(),
28
33
  workspaceToken: e.getWorkspaceToken(),
@@ -30,15 +35,19 @@ function k(e) {
30
35
  organizationId: i()
31
36
  };
32
37
  }
33
- function A() {
38
+ function j() {
34
39
  return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
35
40
  }
36
- function j(e) {
41
+ function M(e, t, n) {
42
+ let r = e.metadata?.writeProposalTransportNote;
43
+ return e.role === "system" && r?.status === "uncertain" && r.proposalId === n && r.sandboxId === t.sandboxId && r.sessionId === t.sessionId;
44
+ }
45
+ function N(e) {
37
46
  let t = /* @__PURE__ */ new Set();
38
47
  for (let n of e.messages) for (let e of _(n.metadata)) t.add(e.proposalId);
39
48
  return t;
40
49
  }
41
- function M(e, t, n) {
50
+ function P(e, t, n) {
42
51
  if (n) {
43
52
  let t = e.messages.find((e) => e.id === n);
44
53
  if (t?.role === "assistant") return t;
@@ -49,11 +58,11 @@ function M(e, t, n) {
49
58
  }
50
59
  return null;
51
60
  }
52
- function N(e) {
61
+ function F(e) {
53
62
  let t = /* @__PURE__ */ new Map();
54
63
  for (let n of e) {
55
64
  if (n.view.status !== "pending") continue;
56
- let e = `${O(n.session)}\u0000${n.session.conversationId}`, r = t.get(e) ?? {
65
+ let e = `${k(n.session)}\u0000${n.session.conversationId}`, r = t.get(e) ?? {
57
66
  session: n.session,
58
67
  proposalIds: []
59
68
  };
@@ -61,124 +70,152 @@ function N(e) {
61
70
  }
62
71
  return t;
63
72
  }
64
- function P(i) {
65
- let { getAccessToken: P, getWorkspaceToken: F, userId: I } = e(), L = l(), R = T({
73
+ function I(i) {
74
+ let { getAccessToken: I, getWorkspaceToken: L, userId: R } = e(), z = u(), B = T({
66
75
  tr: i.tr,
67
76
  productWriteApproval: i.productWriteApproval ?? "legacy",
68
- getAccessToken: P,
69
- getWorkspaceToken: F,
70
- userId: I,
71
- preview: L
77
+ getAccessToken: I,
78
+ getWorkspaceToken: L,
79
+ userId: R,
80
+ preview: z
72
81
  });
73
82
  C(() => {
74
- R.current = {
83
+ B.current = {
75
84
  tr: i.tr,
76
85
  productWriteApproval: i.productWriteApproval ?? "legacy",
77
- getAccessToken: P,
78
- getWorkspaceToken: F,
79
- userId: I,
80
- preview: L
86
+ getAccessToken: I,
87
+ getWorkspaceToken: L,
88
+ userId: R,
89
+ preview: z
81
90
  };
82
91
  });
83
- let [z, B] = E({}), V = T({}), H = T(/* @__PURE__ */ new Map()), U = T(/* @__PURE__ */ new Map()), W = T(/* @__PURE__ */ new Set()), G = T(/* @__PURE__ */ new Map()), K = T(/* @__PURE__ */ new Set()), q = T(null), J = T(/* @__PURE__ */ new Set()), Y = T(/* @__PURE__ */ new Map()), X = T(/* @__PURE__ */ new Set()), [Z, ee] = E(() => /* @__PURE__ */ new Set());
92
+ let [te, V] = E({}), H = T({}), U = T(/* @__PURE__ */ new Map()), W = T(/* @__PURE__ */ new Map()), G = T(/* @__PURE__ */ new Set()), K = T(/* @__PURE__ */ new Map()), q = T(/* @__PURE__ */ new Set()), J = T(null), Y = T(/* @__PURE__ */ new Set()), X = T(/* @__PURE__ */ new Map()), Z = T(/* @__PURE__ */ new Set()), [ne, re] = E(() => /* @__PURE__ */ new Set());
84
93
  C(() => {
85
- let e = K.current, n = t.subscribe((e, t) => {
86
- e.activeConversationId !== t.activeConversationId && W.current.clear();
94
+ let e = q.current, n = t.subscribe((e, t) => {
95
+ e.activeConversationId !== t.activeConversationId && G.current.clear();
87
96
  }), i = (e) => {
88
97
  if (e.length === 0) return;
89
- let n = { ...V.current };
98
+ let n = { ...H.current };
90
99
  for (let r of [...e].sort((e, t) => e.view.createdAt - t.view.createdAt)) {
91
100
  let e = n[r.view.proposalId];
92
- if (!(e && D.has(e.view.status) && !D.has(r.view.status)) && (n[r.view.proposalId] = r, H.current.has(r.view.proposalId) || H.current.set(r.view.proposalId, U.current.get(`${O(r.session)}\u0000${r.session.conversationId}`) ?? u()), r.view.status === "succeeded" && !r.view.result?.errors?.length)) {
93
- let n = H.current.get(r.view.proposalId);
94
- if (!n || !d(n, u())) continue;
95
- let i = t.getState(), a = i.conversations.find((e) => e.id === r.session.conversationId), s = a && (a.messages.find((e) => _(e.metadata).some((e) => e.proposalId === r.view.proposalId)) ?? M(a, r.session, null));
96
- if (!s) continue;
97
- if (i.activeConversationId === r.session.conversationId && R.current.preview?.onEvent) for (let e of p(r.view, r.session.conversationId, s.id, n)) W.current.has(e.action.id) || (W.current.add(e.action.id), f(R.current.preview, e));
98
- if (!e || D.has(e.view.status)) continue;
99
- i.addMessage(r.session.conversationId, {
100
- id: A(),
101
+ if (e && D.has(e.view.status) && !D.has(r.view.status)) continue;
102
+ n[r.view.proposalId] = r;
103
+ let i = !1;
104
+ if (O(r.view.status)) {
105
+ let e = t.getState(), n = e.conversations.find((e) => e.id === r.session.conversationId);
106
+ for (let t of n?.messages ?? []) M(t, r.session, r.view.proposalId) && (e.patchMessage(r.session.conversationId, t.id, {
107
+ content: l(r.view, B.current.tr),
108
+ metadata: {
109
+ ...t.metadata,
110
+ writeProposalTransportNote: {
111
+ proposalId: r.view.proposalId,
112
+ sandboxId: r.session.sandboxId,
113
+ sessionId: r.session.sessionId,
114
+ status: r.view.status
115
+ }
116
+ }
117
+ }), i = !0);
118
+ }
119
+ if (U.current.has(r.view.proposalId) || U.current.set(r.view.proposalId, W.current.get(`${k(r.session)}\u0000${r.session.conversationId}`) ?? d()), r.view.status === "succeeded" && !r.view.result?.errors?.length) {
120
+ let n = U.current.get(r.view.proposalId);
121
+ if (!n || !ee(n, d())) continue;
122
+ let a = t.getState(), s = a.conversations.find((e) => e.id === r.session.conversationId), l = s && (s.messages.find((e) => _(e.metadata).some((e) => e.proposalId === r.view.proposalId)) ?? P(s, r.session, null));
123
+ if (!l) continue;
124
+ if (a.activeConversationId === r.session.conversationId && B.current.preview?.onEvent) for (let e of p(r.view, r.session.conversationId, l.id, n)) G.current.has(e.action.id) || (G.current.add(e.action.id), f(B.current.preview, e));
125
+ if (!e || D.has(e.view.status) || i) continue;
126
+ a.addMessage(r.session.conversationId, {
127
+ id: j(),
101
128
  role: "system",
102
- content: R.current.tr("writeProposal.status.succeeded", "Done — {{summary}}", { summary: o(r.view.result?.summary || r.view.description.readbackPast, c(r.view)) }),
129
+ content: B.current.tr("writeProposal.status.succeeded", "Done — {{summary}}", { summary: o(r.view.result?.summary || r.view.description.readbackPast, c(r.view)) }),
103
130
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
104
131
  });
105
132
  }
106
133
  }
107
- V.current = n, B(n);
134
+ H.current = n, V(n);
108
135
  }, s = (e, n, r) => {
109
136
  let i = t.getState(), a = i.conversations.find((t) => t.id === e.conversationId);
110
137
  if (!a) return;
111
- let o = j(a), s = n.filter((e) => !o.has(e.proposalId) && (e.status === "pending" || e.status === "executing"));
138
+ let o = N(a), s = n.filter((e) => !o.has(e.proposalId) && (e.status === "pending" || e.status === "executing"));
112
139
  if (s.length === 0) return;
113
- let c = M(a, e, r);
140
+ let c = P(a, e, r);
114
141
  c && i.mergeMessageMetadata(a.id, c.id, { writeProposalRefs: [..._(c.metadata), ...s.map((t) => ({
115
142
  proposalId: t.proposalId,
116
143
  sandboxId: e.sandboxId,
117
144
  sessionId: e.sessionId
118
145
  }))] });
119
- }, l = h({
146
+ }, u = h({
120
147
  getWorkspaceId: a,
121
- getAuthContext: () => k(R.current)
148
+ getAuthContext: () => A(B.current)
122
149
  }), m = g({
123
150
  client: {
124
- ...l,
151
+ ...u,
125
152
  list: (e, t) => {
126
- let n = `${O(e)}\u0000${e.conversationId}`;
127
- return U.current.has(n) || U.current.set(n, u()), l.list(e, t);
153
+ let n = `${k(e)}\u0000${e.conversationId}`;
154
+ return W.current.has(n) || W.current.set(n, d()), u.list(e, t);
128
155
  }
129
156
  },
130
157
  capabilitiesOf: (e) => t.getState().sandboxWriteCapabilities[e],
131
- actorOf: () => R.current.userId,
158
+ actorOf: () => B.current.userId,
132
159
  onList: (e, t, n) => {
133
160
  let r = {
134
161
  serverNow: t.serverNow,
135
162
  arrivedAt: Date.now()
136
163
  };
137
- G.current.set(O(e), r), i(t.proposals.map((t) => ({
164
+ K.current.set(k(e), r), i(t.proposals.map((t) => ({
138
165
  view: t,
139
166
  ...r,
140
167
  session: e
141
168
  }))), s(e, t.proposals, n.turnMessageId);
142
169
  },
143
170
  onView: (e, t) => {
144
- let n = V.current[t.proposalId];
171
+ let n = H.current[t.proposalId];
145
172
  i([{
146
173
  view: t,
147
- ...G.current.get(O(e)) ?? {
174
+ ...K.current.get(k(e)) ?? {
148
175
  serverNow: n?.serverNow ?? NaN,
149
176
  arrivedAt: n?.arrivedAt ?? NaN
150
177
  },
151
178
  session: e
152
179
  }]);
153
180
  },
154
- onError: (e, n, i) => {
155
- let a = r(n, i), { tr: o } = R.current;
181
+ onError: (e, n, i, a) => {
182
+ let o = r(n, i), { tr: s } = B.current, c = i === "approve" && n.indeterminate && a;
183
+ if (c) {
184
+ let t = H.current[a];
185
+ if (t?.session.conversationId === e.conversationId && k(t.session) === k(e) && O(t.view.status)) return;
186
+ }
156
187
  t.getState().addMessage(e.conversationId, {
157
- id: A(),
188
+ id: j(),
158
189
  role: "system",
159
- content: o(a.key, a.fallback),
160
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
190
+ content: s(o.key, o.fallback),
191
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
192
+ ...c ? { metadata: { writeProposalTransportNote: {
193
+ proposalId: a,
194
+ sandboxId: e.sandboxId,
195
+ sessionId: e.sessionId,
196
+ status: "uncertain"
197
+ } } } : {}
161
198
  });
162
199
  }
163
200
  });
164
- q.current = m;
165
- let v = Y.current;
201
+ J.current = m;
202
+ let v = X.current;
166
203
  return () => {
167
- m.dispose(), n(), q.current === m && (q.current = null), e.clear(), v.clear();
204
+ m.dispose(), n(), J.current === m && (J.current = null), e.clear(), v.clear();
168
205
  };
169
206
  }, []), C(() => t.subscribe((e, t) => {
170
207
  if (e.sandboxWriteCapabilities === t.sandboxWriteCapabilities) return;
171
- let n = Y.current;
208
+ let n = X.current;
172
209
  for (let [t, r] of [...n]) {
173
210
  let i = e.sandboxWriteCapabilities[r.sandboxId];
174
- i && (n.delete(t), y(i) && (K.current.add(O(r)), q.current?.discover(r, "open")));
211
+ i && (n.delete(t), y(i) && (q.current.add(k(r)), J.current?.discover(r, "open")));
175
212
  }
176
213
  }), []);
177
214
  let Q = (e, t) => {
178
- let n = q.current;
215
+ let n = J.current;
179
216
  if (!n) return;
180
- let r = J.current, i = e.filter((e) => !r.has(e.view.proposalId));
181
- for (let { session: e, proposalIds: a } of N(i).values()) {
217
+ let r = Y.current, i = e.filter((e) => !r.has(e.view.proposalId));
218
+ for (let { session: e, proposalIds: a } of F(i).values()) {
182
219
  for (let e of a) r.add(e);
183
220
  n.declineAll(e, t, "system");
184
221
  }
@@ -187,26 +224,26 @@ function P(i) {
187
224
  $.current = Q;
188
225
  }), C(() => t.subscribe((e, t) => {
189
226
  if (e.conversations === t.conversations) return;
190
- let n = new Set(e.conversations.map((e) => e.id)), r = Object.values(V.current).filter((e) => !n.has(e.session.conversationId));
227
+ let n = new Set(e.conversations.map((e) => e.id)), r = Object.values(H.current).filter((e) => !n.has(e.session.conversationId));
191
228
  r.length > 0 && $.current(r, "conversation_closed");
192
229
  }), []), {
193
230
  panel: w(() => {
194
231
  let e = (e) => t.getState().sandboxWriteCapabilities[e], r = (e) => {
195
- let t = R.current;
232
+ let t = B.current;
196
233
  if (t.productWriteApproval !== "proposal-v1") return;
197
- let r = X.current;
234
+ let r = Z.current;
198
235
  if (r.has(e)) return;
199
236
  let i = a();
200
- i && (r.add(e), n(e, i, m, k(t)).finally(() => r.delete(e)));
237
+ i && (r.add(e), n(e, i, m, A(t)).finally(() => r.delete(e)));
201
238
  };
202
239
  return {
203
240
  afterTurn: (e, t) => {
204
- q.current?.discover(e, "turn", { turnMessageId: t });
241
+ J.current?.discover(e, "turn", { turnMessageId: t });
205
242
  },
206
243
  openConversation: (n) => {
207
- let i = q.current;
244
+ let i = J.current;
208
245
  if (!i) return;
209
- let a = Y.current;
246
+ let a = X.current;
210
247
  for (let [e, t] of [...a]) t.conversationId !== n && a.delete(e);
211
248
  let o = t.getState().conversations.find((e) => e.id === n);
212
249
  if (o) for (let { session: t, issuing: n } of v(o)) {
@@ -215,61 +252,61 @@ function P(i) {
215
252
  i.discover(t, "open");
216
253
  continue;
217
254
  }
218
- o || (a.set(`${O(t)}\u0000${t.conversationId}`, t), n && r(t.sandboxId));
255
+ o || (a.set(`${k(t)}\u0000${t.conversationId}`, t), n && r(t.sandboxId));
219
256
  }
220
257
  },
221
258
  ensureKnown: (t, n) => {
222
- let r = q.current;
223
- if (!r || V.current[t.proposalId] || !y(e(t.sandboxId))) return;
224
- let i = O(t);
225
- K.current.has(i) || (K.current.add(i), r.discover({
259
+ let r = J.current;
260
+ if (!r || H.current[t.proposalId] || !y(e(t.sandboxId))) return;
261
+ let i = k(t);
262
+ q.current.has(i) || (q.current.add(i), r.discover({
226
263
  sandboxId: t.sandboxId,
227
264
  sessionId: t.sessionId,
228
265
  conversationId: n
229
266
  }, "mount"));
230
267
  },
231
268
  approve: async (e, t) => {
232
- let n = V.current[e.proposalId], r = q.current;
269
+ let n = H.current[e.proposalId], r = J.current;
233
270
  !n || !r || await r.approve(n.session, e, t);
234
271
  },
235
272
  decline: async (e, t, n) => {
236
- let r = V.current[e.proposalId], i = q.current;
273
+ let r = H.current[e.proposalId], i = J.current;
237
274
  !r || !i || await i.decline(r.session, e, t, "button", n);
238
275
  },
239
276
  presented: async (e, t) => {
240
- let n = V.current[e.proposalId], r = q.current;
277
+ let n = H.current[e.proposalId], r = J.current;
241
278
  !n || !r || await r.presented(n.session, e, t);
242
279
  },
243
- entry: (e) => V.current[e],
280
+ entry: (e) => H.current[e],
244
281
  liveFor: (t) => {
245
- let n = q.current;
246
- return S(t ? Object.values(V.current).filter((e) => e.session.conversationId === t && !n?.isRetired(e.session.sandboxId)).map((t) => ({
282
+ let n = J.current;
283
+ return S(t ? Object.values(H.current).filter((e) => e.session.conversationId === t && !n?.isRetired(e.session.sandboxId)).map((t) => ({
247
284
  status: t.view.status,
248
285
  sandboxId: t.session.sandboxId,
249
286
  offersConfirm: s(t.view, e(t.session.sandboxId)).offerConfirm
250
287
  })) : []);
251
288
  },
252
289
  declineForReply: async (e, t, n) => {
253
- let r = q.current;
290
+ let r = J.current;
254
291
  if (!r) return;
255
292
  let i = n;
256
- await x([...N(Object.values(V.current).filter((t) => t.session.conversationId === e && !r.isRetired(t.session.sandboxId))).values()].map(({ session: e }) => r.declineAll(e, t, i, { report: !0 })), b);
293
+ await x([...F(Object.values(H.current).filter((t) => t.session.conversationId === e && !r.isRetired(t.session.sandboxId))).values()].map(({ session: e }) => r.declineAll(e, t, i, { report: !0 })), b);
257
294
  },
258
295
  withdraw: (e, t) => {
259
- $.current(Object.values(V.current).filter((t) => t.session.conversationId === e), t);
296
+ $.current(Object.values(H.current).filter((t) => t.session.conversationId === e), t);
260
297
  },
261
298
  retireSandbox: (e) => {
262
- let t = q.current;
263
- !t || t.isRetired(e) || ($.current(Object.values(V.current).filter((t) => t.session.sandboxId === e), "sandbox_replaced"), t.retire(e), ee((t) => {
299
+ let t = J.current;
300
+ !t || t.isRetired(e) || ($.current(Object.values(H.current).filter((t) => t.session.sandboxId === e), "sandbox_replaced"), t.retire(e), re((t) => {
264
301
  let n = new Set(t);
265
302
  return n.add(e), n;
266
303
  }));
267
304
  }
268
305
  };
269
306
  }, []),
270
- entries: z,
271
- retiredSandboxes: Z
307
+ entries: te,
308
+ retiredSandboxes: ne
272
309
  };
273
310
  }
274
311
  //#endregion
275
- export { P as useWriteProposals };
312
+ export { I as useWriteProposals };
@@ -107,7 +107,9 @@ export interface WriteProposalDiscoveryDeps {
107
107
  /** One proposal as the sandbox now reports it. */
108
108
  onView: (session: WriteProposalSession, view: WriteProposalView) => void;
109
109
  /** A refusal or failure the person should be told about. */
110
- onError: (session: WriteProposalSession, error: WriteProposalError, action: WriteProposalFailedAction) => void;
110
+ onError: (session: WriteProposalSession, error: WriteProposalError, action: WriteProposalFailedAction,
111
+ /** Present for the one proposal whose approve answer was lost. */
112
+ proposalId?: string) => void;
111
113
  timers?: WriteProposalTimers;
112
114
  /** How often an executing proposal is re-read. */
113
115
  pollMs?: number;
@@ -168,8 +168,8 @@ function m(e) {
168
168
  s.kind === "settled" ? (x(i, s.proposal), o = !1) : s.proposal ? x(i, s.proposal) : i.executing.add(n.proposalId);
169
169
  } catch (t) {
170
170
  if (m) return;
171
- let n = u(t);
172
- n.current && x(i, n.current), e.onError(i.session, n, "approve");
171
+ let r = u(t);
172
+ r.current && x(i, r.current), e.onError(i.session, r, "approve", n.proposalId);
173
173
  }
174
174
  o && await C(i.session, "refresh");
175
175
  }
@@ -1,5 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { ProfileUser, AuthTokens, ProfileMeta } from './ProfileStorageManager';
3
+ import { AuthActions } from './authProvider/useAuthActions';
3
4
  export type { ProfileUser as User, AuthTokens, ProfileMeta };
4
5
  export interface Account {
5
6
  id: string;
@@ -17,9 +18,11 @@ export interface AuthContextValue {
17
18
  isAuthenticated: boolean;
18
19
  isLoading: boolean;
19
20
  login: (user: ProfileUser, accessToken: string, refreshToken?: string) => void;
20
- logout: (options?: {
21
- authoritative?: boolean;
22
- }) => void;
21
+ /**
22
+ * The hook's own signature, so the context cannot drift from it: it once
23
+ * declared narrower options and rejected `accessToken` (BOFF-9411).
24
+ */
25
+ logout: AuthActions['logout'];
23
26
  updateUser: (user: Partial<ProfileUser>) => void;
24
27
  refreshAccessToken: () => Promise<void>;
25
28
  hasPermission: (permission: string) => boolean;