@buoy-gg/agent-core 7.0.35 → 7.0.36

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.
Files changed (174) hide show
  1. package/README.md +10 -7
  2. package/lib/commonjs/blocks/receipts.js +57 -1
  3. package/lib/commonjs/blocks/receipts.js.map +1 -1
  4. package/lib/commonjs/blocks/types.js +23 -2
  5. package/lib/commonjs/blocks/types.js.map +1 -1
  6. package/lib/commonjs/blocks/uiTool.js +423 -10
  7. package/lib/commonjs/blocks/uiTool.js.map +1 -1
  8. package/lib/commonjs/catalog/catalog.g.js +121 -43
  9. package/lib/commonjs/catalog/catalog.g.js.map +1 -1
  10. package/lib/commonjs/catalog/catalog.source.json +121 -45
  11. package/lib/commonjs/catalog/catalog.types.g.js +44 -0
  12. package/lib/commonjs/catalog/catalog.types.g.js.map +1 -0
  13. package/lib/commonjs/catalog/normalizeParams.js +333 -0
  14. package/lib/commonjs/catalog/normalizeParams.js.map +1 -0
  15. package/lib/commonjs/catalog/signature.js +68 -0
  16. package/lib/commonjs/catalog/signature.js.map +1 -0
  17. package/lib/commonjs/catalog/snapshotReads.js +13 -6
  18. package/lib/commonjs/catalog/snapshotReads.js.map +1 -1
  19. package/lib/commonjs/catalog/toProviderTools.js +41 -5
  20. package/lib/commonjs/catalog/toProviderTools.js.map +1 -1
  21. package/lib/commonjs/context/buildContextPack.js +367 -19
  22. package/lib/commonjs/context/buildContextPack.js.map +1 -1
  23. package/lib/commonjs/effects/digest.js +34 -0
  24. package/lib/commonjs/effects/digest.js.map +1 -0
  25. package/lib/commonjs/effects/ledger.js +85 -1
  26. package/lib/commonjs/effects/ledger.js.map +1 -1
  27. package/lib/commonjs/engine/askGate.js +287 -0
  28. package/lib/commonjs/engine/askGate.js.map +1 -0
  29. package/lib/commonjs/engine/effectFor.js +82 -1
  30. package/lib/commonjs/engine/effectFor.js.map +1 -1
  31. package/lib/commonjs/engine/historyBudget.js +260 -0
  32. package/lib/commonjs/engine/historyBudget.js.map +1 -0
  33. package/lib/commonjs/engine/runAgentTurn.js +924 -83
  34. package/lib/commonjs/engine/runAgentTurn.js.map +1 -1
  35. package/lib/commonjs/engine/systemPrompt.js +72 -8
  36. package/lib/commonjs/engine/systemPrompt.js.map +1 -1
  37. package/lib/commonjs/engine/textToolCalls.js +276 -0
  38. package/lib/commonjs/engine/textToolCalls.js.map +1 -0
  39. package/lib/commonjs/index.js +58 -0
  40. package/lib/commonjs/index.js.map +1 -1
  41. package/lib/commonjs/policy/labels.js +19 -3
  42. package/lib/commonjs/policy/labels.js.map +1 -1
  43. package/lib/commonjs/policy/policy.js +6 -1
  44. package/lib/commonjs/policy/policy.js.map +1 -1
  45. package/lib/commonjs/policy/redact.js +42 -5
  46. package/lib/commonjs/policy/redact.js.map +1 -1
  47. package/lib/commonjs/providers/anthropic.js +331 -208
  48. package/lib/commonjs/providers/anthropic.js.map +1 -1
  49. package/lib/commonjs/providers/openai.js +229 -103
  50. package/lib/commonjs/providers/openai.js.map +1 -1
  51. package/lib/commonjs/providers/sse.js +164 -29
  52. package/lib/commonjs/providers/sse.js.map +1 -1
  53. package/lib/commonjs/providers/streamTimer.js +123 -0
  54. package/lib/commonjs/providers/streamTimer.js.map +1 -0
  55. package/lib/commonjs/providers/transport.js +107 -0
  56. package/lib/commonjs/providers/transport.js.map +1 -0
  57. package/lib/commonjs/providers/xhrStream.js +209 -0
  58. package/lib/commonjs/providers/xhrStream.js.map +1 -0
  59. package/lib/commonjs/session.js +215 -58
  60. package/lib/commonjs/session.js.map +1 -1
  61. package/lib/module/blocks/receipts.js +57 -1
  62. package/lib/module/blocks/receipts.js.map +1 -1
  63. package/lib/module/blocks/types.js +23 -2
  64. package/lib/module/blocks/types.js.map +1 -1
  65. package/lib/module/blocks/uiTool.js +421 -10
  66. package/lib/module/blocks/uiTool.js.map +1 -1
  67. package/lib/module/catalog/catalog.g.js +121 -43
  68. package/lib/module/catalog/catalog.g.js.map +1 -1
  69. package/lib/module/catalog/catalog.source.json +121 -45
  70. package/lib/module/catalog/catalog.types.g.js +40 -0
  71. package/lib/module/catalog/catalog.types.g.js.map +1 -0
  72. package/lib/module/catalog/normalizeParams.js +327 -0
  73. package/lib/module/catalog/normalizeParams.js.map +1 -0
  74. package/lib/module/catalog/signature.js +63 -0
  75. package/lib/module/catalog/signature.js.map +1 -0
  76. package/lib/module/catalog/snapshotReads.js +13 -6
  77. package/lib/module/catalog/snapshotReads.js.map +1 -1
  78. package/lib/module/catalog/toProviderTools.js +41 -5
  79. package/lib/module/catalog/toProviderTools.js.map +1 -1
  80. package/lib/module/context/buildContextPack.js +366 -19
  81. package/lib/module/context/buildContextPack.js.map +1 -1
  82. package/lib/module/effects/digest.js +29 -0
  83. package/lib/module/effects/digest.js.map +1 -0
  84. package/lib/module/effects/ledger.js +85 -1
  85. package/lib/module/effects/ledger.js.map +1 -1
  86. package/lib/module/engine/askGate.js +280 -0
  87. package/lib/module/engine/askGate.js.map +1 -0
  88. package/lib/module/engine/effectFor.js +83 -1
  89. package/lib/module/engine/effectFor.js.map +1 -1
  90. package/lib/module/engine/historyBudget.js +251 -0
  91. package/lib/module/engine/historyBudget.js.map +1 -0
  92. package/lib/module/engine/runAgentTurn.js +920 -83
  93. package/lib/module/engine/runAgentTurn.js.map +1 -1
  94. package/lib/module/engine/systemPrompt.js +71 -8
  95. package/lib/module/engine/systemPrompt.js.map +1 -1
  96. package/lib/module/engine/textToolCalls.js +270 -0
  97. package/lib/module/engine/textToolCalls.js.map +1 -0
  98. package/lib/module/index.js +5 -3
  99. package/lib/module/index.js.map +1 -1
  100. package/lib/module/policy/labels.js +19 -3
  101. package/lib/module/policy/labels.js.map +1 -1
  102. package/lib/module/policy/policy.js +6 -1
  103. package/lib/module/policy/policy.js.map +1 -1
  104. package/lib/module/policy/redact.js +42 -5
  105. package/lib/module/policy/redact.js.map +1 -1
  106. package/lib/module/providers/anthropic.js +332 -209
  107. package/lib/module/providers/anthropic.js.map +1 -1
  108. package/lib/module/providers/openai.js +230 -104
  109. package/lib/module/providers/openai.js.map +1 -1
  110. package/lib/module/providers/sse.js +160 -29
  111. package/lib/module/providers/sse.js.map +1 -1
  112. package/lib/module/providers/streamTimer.js +118 -0
  113. package/lib/module/providers/streamTimer.js.map +1 -0
  114. package/lib/module/providers/transport.js +103 -0
  115. package/lib/module/providers/transport.js.map +1 -0
  116. package/lib/module/providers/xhrStream.js +204 -0
  117. package/lib/module/providers/xhrStream.js.map +1 -0
  118. package/lib/module/session.js +199 -60
  119. package/lib/module/session.js.map +1 -1
  120. package/lib/typescript/blocks/receipts.d.ts +5 -0
  121. package/lib/typescript/blocks/receipts.d.ts.map +1 -1
  122. package/lib/typescript/blocks/types.d.ts +23 -2
  123. package/lib/typescript/blocks/types.d.ts.map +1 -1
  124. package/lib/typescript/blocks/uiTool.d.ts +11 -2
  125. package/lib/typescript/blocks/uiTool.d.ts.map +1 -1
  126. package/lib/typescript/catalog/catalog.g.d.ts.map +1 -1
  127. package/lib/typescript/catalog/catalog.types.g.d.ts +40 -0
  128. package/lib/typescript/catalog/catalog.types.g.d.ts.map +1 -0
  129. package/lib/typescript/catalog/normalizeParams.d.ts +43 -0
  130. package/lib/typescript/catalog/normalizeParams.d.ts.map +1 -0
  131. package/lib/typescript/catalog/signature.d.ts +29 -0
  132. package/lib/typescript/catalog/signature.d.ts.map +1 -0
  133. package/lib/typescript/catalog/snapshotReads.d.ts.map +1 -1
  134. package/lib/typescript/catalog/toProviderTools.d.ts +28 -15
  135. package/lib/typescript/catalog/toProviderTools.d.ts.map +1 -1
  136. package/lib/typescript/context/buildContextPack.d.ts +24 -2
  137. package/lib/typescript/context/buildContextPack.d.ts.map +1 -1
  138. package/lib/typescript/effects/digest.d.ts +10 -0
  139. package/lib/typescript/effects/digest.d.ts.map +1 -0
  140. package/lib/typescript/effects/ledger.d.ts +55 -0
  141. package/lib/typescript/effects/ledger.d.ts.map +1 -1
  142. package/lib/typescript/engine/askGate.d.ts +29 -0
  143. package/lib/typescript/engine/askGate.d.ts.map +1 -0
  144. package/lib/typescript/engine/effectFor.d.ts +6 -1
  145. package/lib/typescript/engine/effectFor.d.ts.map +1 -1
  146. package/lib/typescript/engine/historyBudget.d.ts +92 -0
  147. package/lib/typescript/engine/historyBudget.d.ts.map +1 -0
  148. package/lib/typescript/engine/runAgentTurn.d.ts +83 -1
  149. package/lib/typescript/engine/runAgentTurn.d.ts.map +1 -1
  150. package/lib/typescript/engine/systemPrompt.d.ts +41 -0
  151. package/lib/typescript/engine/systemPrompt.d.ts.map +1 -1
  152. package/lib/typescript/engine/textToolCalls.d.ts +58 -0
  153. package/lib/typescript/engine/textToolCalls.d.ts.map +1 -0
  154. package/lib/typescript/index.d.ts +5 -3
  155. package/lib/typescript/index.d.ts.map +1 -1
  156. package/lib/typescript/policy/labels.d.ts.map +1 -1
  157. package/lib/typescript/policy/policy.d.ts.map +1 -1
  158. package/lib/typescript/policy/redact.d.ts.map +1 -1
  159. package/lib/typescript/providers/anthropic.d.ts.map +1 -1
  160. package/lib/typescript/providers/openai.d.ts +0 -16
  161. package/lib/typescript/providers/openai.d.ts.map +1 -1
  162. package/lib/typescript/providers/sse.d.ts +82 -1
  163. package/lib/typescript/providers/sse.d.ts.map +1 -1
  164. package/lib/typescript/providers/streamTimer.d.ts +59 -0
  165. package/lib/typescript/providers/streamTimer.d.ts.map +1 -0
  166. package/lib/typescript/providers/transport.d.ts +40 -0
  167. package/lib/typescript/providers/transport.d.ts.map +1 -0
  168. package/lib/typescript/providers/types.d.ts +91 -3
  169. package/lib/typescript/providers/types.d.ts.map +1 -1
  170. package/lib/typescript/providers/xhrStream.d.ts +43 -0
  171. package/lib/typescript/providers/xhrStream.d.ts.map +1 -0
  172. package/lib/typescript/session.d.ts +40 -2
  173. package/lib/typescript/session.d.ts.map +1 -1
  174. package/package.json +2 -2
@@ -3,13 +3,32 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "compressConsumed", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _historyBudget.compressConsumed;
10
+ }
11
+ });
6
12
  exports.createAskBuoySession = createAskBuoySession;
7
13
  exports.resumableHistory = resumableHistory;
14
+ Object.defineProperty(exports, "trimHistory", {
15
+ enumerable: true,
16
+ get: function () {
17
+ return _historyBudget.trimHistory;
18
+ }
19
+ });
20
+ Object.defineProperty(exports, "trimHistoryReport", {
21
+ enumerable: true,
22
+ get: function () {
23
+ return _historyBudget.trimHistoryReport;
24
+ }
25
+ });
8
26
  var _catalog = require("./catalog/catalog.g");
9
27
  var _buildContextPack = require("./context/buildContextPack");
10
28
  var _runAgentTurn = require("./engine/runAgentTurn");
11
29
  var _systemPrompt = require("./engine/systemPrompt");
12
30
  var _ledger = require("./effects/ledger");
31
+ var _historyBudget = require("./engine/historyBudget");
13
32
  var _anthropic = require("./providers/anthropic");
14
33
  var _openai = require("./providers/openai");
15
34
  var _policy = require("./policy/policy");
@@ -20,43 +39,11 @@ var _redact = require("./policy/redact");
20
39
  * This is the one-call surface the UI packages use. It owns the pieces that
21
40
  * have to agree with each other across a whole session — the provider, the
22
41
  * catalog filtered to what this device can actually do, the system prompt
23
- * (built once, after grounding), the effect ledger, and the message history —
42
+ * (the stable part built once after grounding, the RIGHT NOW part re-read every
43
+ * turn), the effect ledger, and the message history —
24
44
  * so a chat UI is a message list plus a text box rather than an orchestrator.
25
45
  */
26
46
 
27
- /**
28
- * A long QA session must not die on the provider's context limit with an
29
- * opaque error — the transcript grows without bound (tool results are the
30
- * heavy part), so the OLDEST rounds are dropped once the history passes a
31
- * budget. Rounds are dropped whole — a round starts at a user message — so
32
- * the assistant/tool-result pairing every provider requires stays intact and
33
- * the history still begins with a user message.
34
- */
35
- const MAX_HISTORY_CHARS = 150_000;
36
- const MIN_KEEP_MESSAGES = 4;
37
- function messageSize(m) {
38
- try {
39
- return JSON.stringify(m).length;
40
- } catch {
41
- return 0;
42
- }
43
- }
44
- function trimHistory(messages) {
45
- let total = 0;
46
- for (const m of messages) total += messageSize(m);
47
- if (total <= MAX_HISTORY_CHARS) return messages;
48
- const out = [...messages];
49
- while (out.length > MIN_KEEP_MESSAGES && total > MAX_HISTORY_CHARS) {
50
- total -= messageSize(out[0]);
51
- out.shift();
52
- while (out.length > MIN_KEEP_MESSAGES && out[0].role !== "user") {
53
- total -= messageSize(out[0]);
54
- out.shift();
55
- }
56
- }
57
- return out;
58
- }
59
-
60
47
  /**
61
48
  * A stored history, made safe to send as the start of a new request.
62
49
  *
@@ -73,6 +60,9 @@ function trimHistory(messages) {
73
60
  * always resumes with a fresh user message instead, so their signatures are
74
61
  * dead weight at best and a stale-signature 400 at worst.
75
62
  */
63
+ // Re-exported: these were defined here before the engine needed them too, and
64
+ // the tests and the package surface still reach them through this module.
65
+
76
66
  function resumableHistory(history) {
77
67
  let end = history.length;
78
68
  while (end > 0) {
@@ -90,8 +80,50 @@ function createAskBuoySession(config) {
90
80
  const ledger = new _ledger.EffectLedger();
91
81
  const policy = config.policy ?? {};
92
82
  let messages = [];
83
+ /**
84
+ * Image-URL provenance for the whole conversation, not one turn of it.
85
+ *
86
+ * "Show me the shop as pictures" and "no, actually render them" are two
87
+ * turns; the second has no tool calls of its own, so a per-turn set held
88
+ * nothing and every image was stripped out from under the model. It kept
89
+ * describing artwork that was not on screen. The rule is unchanged — a URL
90
+ * still has to have been READ — the window is just the conversation. Reset
91
+ * with the conversation.
92
+ */
93
+ const seenUrls = new Set();
93
94
  let system;
94
95
  let primed = false;
96
+ /** See `setApprovalBypass`. Consulted per turn, never captured. */
97
+ let bypassApproval = false;
98
+ /** See `setReadOnly`. */
99
+ let deviceReadOnly = false;
100
+ /**
101
+ * Rounds dropped by `restore` — reported on the first send, because there is
102
+ * no open turn to carry the event when the restore happens.
103
+ */
104
+ let trimmedOnRestore = 0;
105
+ /**
106
+ * Which conversation is live. Bumped by `reset`.
107
+ *
108
+ * A turn's history is committed when its generator drains, and a turn does
109
+ * not stop the moment Stop is pressed — an in-flight dispatch has to settle
110
+ * first, and nothing can pull a store write back out of an adapter. So the
111
+ * sequence Stop -> "New conversation" -> the write finishes used to end with
112
+ * the OLD turn assigning its whole history over the empty one: a chat the
113
+ * user had just cleared came back, carrying a change they never saw made.
114
+ * The turn still finishes and its effect still lands in the app; it simply
115
+ * no longer owns a conversation that has moved on.
116
+ */
117
+ let generation = 0;
118
+ /**
119
+ * The grounding probe's result, held past `prime`.
120
+ *
121
+ * The system prompt is a pure function of this plus the policy, and the
122
+ * approval paragraph changes when the device toggle does — holding the
123
+ * digest lets the prompt be rebuilt without re-probing the app for state it
124
+ * already read.
125
+ */
126
+ let digest = {};
95
127
  /**
96
128
  * `storage key -> the live store that owns it`, derived from the grounding.
97
129
  *
@@ -105,16 +137,62 @@ function createAskBuoySession(config) {
105
137
  // So the agent's own requests to the model endpoint never come back to it
106
138
  // out of the network tool — that would feed it its own auth headers.
107
139
  (0, _redact.registerSelfEndpoint)(config.endpoint);
108
- const provider = config.protocol === "openai" ? (0, _openai.createOpenAIProvider)(config) : (0, _anthropic.createAnthropicProvider)(config);
140
+ const provider = config.provider ?? (config.protocol === "openai" ? (0, _openai.createOpenAIProvider)(config) : (0, _anthropic.createAnthropicProvider)(config));
109
141
 
110
142
  // Mutable: the device's tool list fills in over the first frames and can
111
143
  // change while the sheet is open. See setAvailableActions.
112
144
  let availableActions = config.availableActions;
113
145
  let availableToolIds = Object.keys(availableActions);
146
+
147
+ /**
148
+ * THE policy object the engine reads, mutated in place.
149
+ *
150
+ * One object for the life of the session, not a fresh one per turn: the
151
+ * engine reads `policy.readOnly` and the approval lists at each call, so a
152
+ * toggle flipped mid-turn lands on the very next call of that turn. A new
153
+ * object per turn would leave a running turn on the old rules until it
154
+ * finished — "Read only" switched on while the agent was writing would let
155
+ * the rest of the batch through.
156
+ */
157
+ const live = {
158
+ ...policy
159
+ };
160
+ function refreshLive() {
161
+ for (const k of Object.keys(live)) delete live[k];
162
+ Object.assign(live, policy);
163
+ if (bypassApproval) {
164
+ live.requireApproval = [];
165
+ live.requireApprovalFor = [];
166
+ }
167
+ // OR, never override: the device can narrow, the app's own read-only stands.
168
+ if (deviceReadOnly) live.readOnly = true;
169
+ }
170
+ function activePolicy() {
171
+ return live;
172
+ }
173
+ function composeSystem() {
174
+ system = (0, _systemPrompt.buildSystemPrompt)({
175
+ catalog,
176
+ context: {
177
+ ...config.context,
178
+ digest: {
179
+ ...digest,
180
+ ...config.context?.digest
181
+ }
182
+ },
183
+ isRelease: config.isRelease,
184
+ readOnly: live.readOnly ?? false,
185
+ readOnlySource: policy.readOnly ? "app" : deviceReadOnly ? "device" : undefined,
186
+ requireApproval: (0, _policy.resolveRequireApproval)(live),
187
+ approvalsBypassed: bypassApproval,
188
+ appName: config.appName,
189
+ platform: config.platform
190
+ });
191
+ }
114
192
  async function prime() {
115
193
  if (primed) return;
116
194
  primed = true;
117
- let digest = {};
195
+ digest = {};
118
196
  try {
119
197
  digest = await (0, _buildContextPack.buildContextDigest)({
120
198
  dispatch: config.dispatch,
@@ -131,21 +209,7 @@ function createAskBuoySession(config) {
131
209
  const store = raw;
132
210
  if (store?.persistsTo && store.name) storeKeyOwners[store.persistsTo] = store.name;
133
211
  }
134
- system = (0, _systemPrompt.buildSystemPrompt)({
135
- catalog,
136
- context: {
137
- ...config.context,
138
- digest: {
139
- ...digest,
140
- ...config.context?.digest
141
- }
142
- },
143
- isRelease: config.isRelease,
144
- readOnly: policy.readOnly ?? false,
145
- requireApproval: (0, _policy.resolveRequireApproval)(policy),
146
- appName: config.appName,
147
- platform: config.platform
148
- });
212
+ composeSystem();
149
213
  }
150
214
  return {
151
215
  ledger,
@@ -157,35 +221,122 @@ function createAskBuoySession(config) {
157
221
  availableActions = actions;
158
222
  availableToolIds = Object.keys(actions);
159
223
  },
224
+ setApprovalBypass(enabled) {
225
+ if (bypassApproval === enabled) return;
226
+ bypassApproval = enabled;
227
+ refreshLive();
228
+ // The prompt tells the model whether a card will appear before its
229
+ // change lands; leaving that paragraph stale would have it announce an
230
+ // approval step that no longer exists. Rebuilt from the held digest, so
231
+ // toggling mid-conversation costs nothing and re-reads nothing.
232
+ if (system) composeSystem();
233
+ },
234
+ setReadOnly(enabled) {
235
+ if (deviceReadOnly === enabled) return;
236
+ deviceReadOnly = enabled;
237
+ refreshLive();
238
+ if (system) composeSystem();
239
+ },
160
240
  restore(history) {
161
241
  // Only into a session that has not been talked to. Restoring over a live
162
242
  // conversation would drop whatever was said since the app started.
163
243
  if (messages.length > 0) return;
164
- messages = trimHistory(resumableHistory(history));
244
+ const report = (0, _historyBudget.trimHistoryReport)(resumableHistory(history));
245
+ messages = report.messages;
246
+ trimmedOnRestore = report.droppedRounds;
165
247
  },
166
248
  async *send(text, signal) {
249
+ /**
250
+ * Captured FIRST, before any await.
251
+ *
252
+ * Priming and the live-context probes are awaits like any other, and a
253
+ * reset can land inside them. Reading the generation after them would
254
+ * pick up the NEW conversation's value and let this turn commit into it —
255
+ * the precise bug this guards against, reintroduced one line lower.
256
+ */
257
+ const myGeneration = generation;
167
258
  await prime();
168
- messages.push({
259
+ // The RIGHT NOW block: re-read on EVERY turn, not once at open. The
260
+ // stable digest above was once the whole picture, and it was built
261
+ // before the user had navigated anywhere — so the query that owned the
262
+ // screen they were looking at was simply not in the prompt. This is the
263
+ // uncached tail of the system prompt (see ProviderRequest.systemVolatile).
264
+ let live;
265
+ try {
266
+ live = await (0, _buildContextPack.buildLiveContext)({
267
+ dispatch: config.dispatch,
268
+ readSnapshot: config.readSnapshot,
269
+ availableToolIds,
270
+ availableActions
271
+ });
272
+ } catch {
273
+ live = undefined;
274
+ }
275
+ /**
276
+ * The conversation was cleared while this turn was still getting ready.
277
+ *
278
+ * Priming and the live probes take real time, so "New conversation"
279
+ * lands here often enough to matter. Abandoning now costs nothing: the
280
+ * model has not been asked anything yet.
281
+ */
282
+ if (myGeneration !== generation) {
283
+ yield {
284
+ type: "done"
285
+ };
286
+ return;
287
+ }
288
+
289
+ /**
290
+ * The turn works on its OWN array, and `messages` is assigned in exactly
291
+ * one place — the commit at the bottom.
292
+ *
293
+ * `runAgentTurn` pushes each round into the array it is handed. Handing
294
+ * it the session's own array made every push a live edit of session
295
+ * state, so a turn racing a reset wrote its whole history into the new
296
+ * conversation one push at a time and the commit guard had nothing left
297
+ * to protect. Copying makes ownership checkable in one place instead of
298
+ * spread across the loop.
299
+ */
300
+ const trimmed = (0, _historyBudget.trimHistoryReport)([...messages, {
169
301
  role: "user",
170
302
  text
171
- });
172
- messages = trimHistory(messages);
303
+ }]);
304
+ const working = trimmed.messages;
305
+ // Said once, before the answer it affects. The screen still shows the
306
+ // rounds the model just lost; this is the only thing that tells the two
307
+ // apart. See the `history-trimmed` event.
308
+ const droppedRounds = trimmed.droppedRounds + trimmedOnRestore;
309
+ trimmedOnRestore = 0;
310
+ if (droppedRounds > 0) yield {
311
+ type: "history-trimmed",
312
+ droppedRounds
313
+ };
173
314
  const gen = (0, _runAgentTurn.runAgentTurn)({
174
315
  provider,
175
316
  catalog,
176
- messages,
317
+ messages: working,
177
318
  system: system,
319
+ systemVolatile: (0, _systemPrompt.buildLiveBlock)(live,
320
+ // What this conversation has already done to the app. See
321
+ // buildLiveBlock — without it the model answers "what have you
322
+ // changed?" from memory of its own sentences, and reports a value it
323
+ // overwrote itself as the app's own.
324
+ ledger.list().map(e => ({
325
+ label: `${e.toolId}.${e.action}${e.effect.label ? ` — ${e.effect.label}` : ""}`,
326
+ undone: e.undoneAt !== undefined
327
+ }))),
178
328
  model: config.model,
179
329
  maxTokens: config.maxTokens ?? 4096,
180
330
  dispatch: config.dispatch,
181
331
  readSnapshot: config.readSnapshot,
182
332
  storeKeyOwners,
183
333
  ledger,
184
- policy,
334
+ policy: activePolicy(),
185
335
  isRelease: config.isRelease,
186
336
  availableToolIds,
187
337
  availableActions,
188
338
  requestApproval: config.requestApproval,
339
+ seenUrls,
189
340
  signal
190
341
  });
191
342
  let next = await gen.next();
@@ -194,11 +345,17 @@ function createAskBuoySession(config) {
194
345
  next = await gen.next();
195
346
  }
196
347
  // The generator's return value is the full history including tool turns.
197
- messages = next.value;
348
+ // Committed only if this turn still owns the conversation — see
349
+ // `generation`.
350
+ if (myGeneration === generation) messages = next.value;
198
351
  },
199
352
  reset() {
353
+ // Before anything else: a turn still draining must not commit over this.
354
+ generation += 1;
200
355
  messages = [];
356
+ trimmedOnRestore = 0;
201
357
  ledger.clear();
358
+ seenUrls.clear();
202
359
  // The grounding digest is a snapshot of the app AT OPEN — stores and
203
360
  // routes registered since are invisible to it. A new conversation is
204
361
  // the natural moment to re-read the world.
@@ -1 +1 @@
1
- {"version":3,"names":["_catalog","require","_buildContextPack","_runAgentTurn","_systemPrompt","_ledger","_anthropic","_openai","_policy","_redact","MAX_HISTORY_CHARS","MIN_KEEP_MESSAGES","messageSize","m","JSON","stringify","length","trimHistory","messages","total","out","shift","role","resumableHistory","history","end","toolCalls","slice","map","thinking","undefined","createAskBuoySession","config","catalog","CATALOG","ledger","EffectLedger","policy","system","primed","storeKeyOwners","registerSelfEndpoint","endpoint","provider","protocol","createOpenAIProvider","createAnthropicProvider","availableActions","availableToolIds","Object","keys","prime","digest","buildContextDigest","dispatch","readSnapshot","raw","zustandStores","store","persistsTo","name","buildSystemPrompt","context","isRelease","readOnly","requireApproval","resolveRequireApproval","appName","platform","setAvailableActions","actions","restore","send","text","signal","push","gen","runAgentTurn","model","maxTokens","requestApproval","next","done","value","reset","clear"],"sourceRoot":"../../src","sources":["session.ts"],"mappings":";;;;;;;AASA,IAAAA,QAAA,GAAAC,OAAA;AACA,IAAAC,iBAAA,GAAAD,OAAA;AACA,IAAAE,aAAA,GAAAF,OAAA;AACA,IAAAG,aAAA,GAAAH,OAAA;AACA,IAAAI,OAAA,GAAAJ,OAAA;AACA,IAAAK,UAAA,GAAAL,OAAA;AACA,IAAAM,OAAA,GAAAN,OAAA;AAEA,IAAAO,OAAA,GAAAP,OAAA;AACA,IAAAQ,OAAA,GAAAR,OAAA;AAlBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMS,iBAAiB,GAAG,OAAO;AACjC,MAAMC,iBAAiB,GAAG,CAAC;AAE3B,SAASC,WAAWA,CAACC,CAAe,EAAU;EAC5C,IAAI;IACF,OAAOC,IAAI,CAACC,SAAS,CAACF,CAAC,CAAC,CAACG,MAAM;EACjC,CAAC,CAAC,MAAM;IACN,OAAO,CAAC;EACV;AACF;AAEA,SAASC,WAAWA,CAACC,QAAwB,EAAkB;EAC7D,IAAIC,KAAK,GAAG,CAAC;EACb,KAAK,MAAMN,CAAC,IAAIK,QAAQ,EAAEC,KAAK,IAAIP,WAAW,CAACC,CAAC,CAAC;EACjD,IAAIM,KAAK,IAAIT,iBAAiB,EAAE,OAAOQ,QAAQ;EAE/C,MAAME,GAAG,GAAG,CAAC,GAAGF,QAAQ,CAAC;EACzB,OAAOE,GAAG,CAACJ,MAAM,GAAGL,iBAAiB,IAAIQ,KAAK,GAAGT,iBAAiB,EAAE;IAClES,KAAK,IAAIP,WAAW,CAACQ,GAAG,CAAC,CAAC,CAAE,CAAC;IAC7BA,GAAG,CAACC,KAAK,CAAC,CAAC;IACX,OAAOD,GAAG,CAACJ,MAAM,GAAGL,iBAAiB,IAAIS,GAAG,CAAC,CAAC,CAAC,CAAEE,IAAI,KAAK,MAAM,EAAE;MAChEH,KAAK,IAAIP,WAAW,CAACQ,GAAG,CAAC,CAAC,CAAE,CAAC;MAC7BA,GAAG,CAACC,KAAK,CAAC,CAAC;IACb;EACF;EACA,OAAOD,GAAG;AACZ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,gBAAgBA,CAACC,OAAuB,EAAkB;EACxE,IAAIC,GAAG,GAAGD,OAAO,CAACR,MAAM;EACxB,OAAOS,GAAG,GAAG,CAAC,EAAE;IACd,MAAMZ,CAAC,GAAGW,OAAO,CAACC,GAAG,GAAG,CAAC,CAAE;IAC3B,IAAIZ,CAAC,CAACS,IAAI,KAAK,WAAW,IAAI,CAACT,CAAC,CAACa,SAAS,EAAEV,MAAM,EAAE;IACpDS,GAAG,IAAI,CAAC;EACV;EACA,OAAOD,OAAO,CAACG,KAAK,CAAC,CAAC,EAAEF,GAAG,CAAC,CAACG,GAAG,CAAEf,CAAC,IACjCA,CAAC,CAACS,IAAI,KAAK,WAAW,IAAIT,CAAC,CAACgB,QAAQ,GAAG;IAAE,GAAGhB,CAAC;IAAEgB,QAAQ,EAAEC;EAAU,CAAC,GAAGjB,CACzE,CAAC;AACH;AAEO,SAASkB,oBAAoBA,CAACC,MAA4B,EAAkB;EACjF,MAAMC,OAAO,GAAGD,MAAM,CAACC,OAAO,IAAIC,gBAAO;EACzC,MAAMC,MAAM,GAAG,IAAIC,oBAAY,CAAC,CAAC;EACjC,MAAMC,MAAM,GAAGL,MAAM,CAACK,MAAM,IAAI,CAAC,CAAC;EAClC,IAAInB,QAAwB,GAAG,EAAE;EACjC,IAAIoB,MAA0B;EAC9B,IAAIC,MAAM,GAAG,KAAK;EAClB;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACE,IAAIC,cAAsC,GAAG,CAAC,CAAC;;EAE/C;EACA;EACA,IAAAC,4BAAoB,EAACT,MAAM,CAACU,QAAQ,CAAC;EAErC,MAAMC,QAAkB,GACtBX,MAAM,CAACY,QAAQ,KAAK,QAAQ,GACxB,IAAAC,4BAAoB,EAACb,MAAM,CAAC,GAC5B,IAAAc,kCAAuB,EAACd,MAAM,CAAC;;EAErC;EACA;EACA,IAAIe,gBAAgB,GAAGf,MAAM,CAACe,gBAAgB;EAC9C,IAAIC,gBAAgB,GAAGC,MAAM,CAACC,IAAI,CAACH,gBAAgB,CAAC;EAEpD,eAAeI,KAAKA,CAAA,EAAkB;IACpC,IAAIZ,MAAM,EAAE;IACZA,MAAM,GAAG,IAAI;IACb,IAAIa,MAA+B,GAAG,CAAC,CAAC;IACxC,IAAI;MACFA,MAAM,GAAG,MAAM,IAAAC,oCAAkB,EAAC;QAChCC,QAAQ,EAAEtB,MAAM,CAACsB,QAAQ;QACzBC,YAAY,EAAEvB,MAAM,CAACuB,YAAY;QACjCP,gBAAgB;QAChBD;MACF,CAAC,CAAC;IACJ,CAAC,CAAC,MAAM;MACN;MACA;IAAA;IAEFP,cAAc,GAAG,CAAC,CAAC;IACnB,KAAK,MAAMgB,GAAG,IAAKJ,MAAM,CAACK,aAAa,IAA8B,EAAE,EAAE;MACvE,MAAMC,KAAK,GAAGF,GAA6C;MAC3D,IAAIE,KAAK,EAAEC,UAAU,IAAID,KAAK,CAACE,IAAI,EAAEpB,cAAc,CAACkB,KAAK,CAACC,UAAU,CAAC,GAAGD,KAAK,CAACE,IAAI;IACpF;IAEAtB,MAAM,GAAG,IAAAuB,+BAAiB,EAAC;MACzB5B,OAAO;MACP6B,OAAO,EAAE;QAAE,GAAG9B,MAAM,CAAC8B,OAAO;QAAEV,MAAM,EAAE;UAAE,GAAGA,MAAM;UAAE,GAAGpB,MAAM,CAAC8B,OAAO,EAAEV;QAAO;MAAE,CAAC;MAChFW,SAAS,EAAE/B,MAAM,CAAC+B,SAAS;MAC3BC,QAAQ,EAAE3B,MAAM,CAAC2B,QAAQ,IAAI,KAAK;MAClCC,eAAe,EAAE,IAAAC,8BAAsB,EAAC7B,MAAM,CAAC;MAC/C8B,OAAO,EAAEnC,MAAM,CAACmC,OAAO;MACvBC,QAAQ,EAAEpC,MAAM,CAACoC;IACnB,CAAC,CAAC;EACJ;EAEA,OAAO;IACLjC,MAAM;IACN,IAAIjB,QAAQA,CAAA,EAAG;MACb,OAAOA,QAAQ;IACjB,CAAC;IACDiC,KAAK;IAELkB,mBAAmBA,CAACC,OAAiC,EAAE;MACrDvB,gBAAgB,GAAGuB,OAAO;MAC1BtB,gBAAgB,GAAGC,MAAM,CAACC,IAAI,CAACoB,OAAO,CAAC;IACzC,CAAC;IAEDC,OAAOA,CAAC/C,OAAuB,EAAE;MAC/B;MACA;MACA,IAAIN,QAAQ,CAACF,MAAM,GAAG,CAAC,EAAE;MACzBE,QAAQ,GAAGD,WAAW,CAACM,gBAAgB,CAACC,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,OAAOgD,IAAIA,CAACC,IAAY,EAAEC,MAAoB,EAAE;MAC9C,MAAMvB,KAAK,CAAC,CAAC;MACbjC,QAAQ,CAACyD,IAAI,CAAC;QAAErD,IAAI,EAAE,MAAM;QAAEmD;MAAK,CAAC,CAAC;MACrCvD,QAAQ,GAAGD,WAAW,CAACC,QAAQ,CAAC;MAEhC,MAAM0D,GAAG,GAAG,IAAAC,0BAAY,EAAC;QACvBlC,QAAQ;QACRV,OAAO;QACPf,QAAQ;QACRoB,MAAM,EAAEA,MAAO;QACfwC,KAAK,EAAE9C,MAAM,CAAC8C,KAAK;QACnBC,SAAS,EAAE/C,MAAM,CAAC+C,SAAS,IAAI,IAAI;QACnCzB,QAAQ,EAAEtB,MAAM,CAACsB,QAAQ;QACzBC,YAAY,EAAEvB,MAAM,CAACuB,YAAY;QACjCf,cAAc;QACdL,MAAM;QACNE,MAAM;QACN0B,SAAS,EAAE/B,MAAM,CAAC+B,SAAS;QAC3Bf,gBAAgB;QAChBD,gBAAgB;QAChBiC,eAAe,EAAEhD,MAAM,CAACgD,eAAe;QACvCN;MACF,CAAC,CAAC;MAEF,IAAIO,IAAI,GAAG,MAAML,GAAG,CAACK,IAAI,CAAC,CAAC;MAC3B,OAAO,CAACA,IAAI,CAACC,IAAI,EAAE;QACjB,MAAMD,IAAI,CAACE,KAAK;QAChBF,IAAI,GAAG,MAAML,GAAG,CAACK,IAAI,CAAC,CAAC;MACzB;MACA;MACA/D,QAAQ,GAAG+D,IAAI,CAACE,KAAK;IACvB,CAAC;IAEDC,KAAKA,CAAA,EAAG;MACNlE,QAAQ,GAAG,EAAE;MACbiB,MAAM,CAACkD,KAAK,CAAC,CAAC;MACd;MACA;MACA;MACA9C,MAAM,GAAG,KAAK;IAChB;EACF,CAAC;AACH","ignoreList":[]}
1
+ {"version":3,"names":["_catalog","require","_buildContextPack","_runAgentTurn","_systemPrompt","_ledger","_historyBudget","_anthropic","_openai","_policy","_redact","resumableHistory","history","end","length","m","role","toolCalls","slice","map","thinking","undefined","createAskBuoySession","config","catalog","CATALOG","ledger","EffectLedger","policy","messages","seenUrls","Set","system","primed","bypassApproval","deviceReadOnly","trimmedOnRestore","generation","digest","storeKeyOwners","registerSelfEndpoint","endpoint","provider","protocol","createOpenAIProvider","createAnthropicProvider","availableActions","availableToolIds","Object","keys","live","refreshLive","k","assign","requireApproval","requireApprovalFor","readOnly","activePolicy","composeSystem","buildSystemPrompt","context","isRelease","readOnlySource","resolveRequireApproval","approvalsBypassed","appName","platform","prime","buildContextDigest","dispatch","readSnapshot","raw","zustandStores","store","persistsTo","name","setAvailableActions","actions","setApprovalBypass","enabled","setReadOnly","restore","report","trimHistoryReport","droppedRounds","send","text","signal","myGeneration","buildLiveContext","type","trimmed","working","gen","runAgentTurn","systemVolatile","buildLiveBlock","list","e","label","toolId","action","effect","undone","undoneAt","model","maxTokens","requestApproval","next","done","value","reset","clear"],"sourceRoot":"../../src","sources":["session.ts"],"mappings":"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","ignoreList":[]}
@@ -44,6 +44,30 @@ function requestSummary(r) {
44
44
  };
45
45
  }
46
46
 
47
+ /**
48
+ * The keys a partial MERGE patch actually changed.
49
+ *
50
+ * A merge cannot remove a key, so a key the patch omits is a key that did not
51
+ * change — and must not be reported. Diffing a store against the patch instead
52
+ * of against the store did exactly that: a one-field bag edit rendered
53
+ * "− card_visa", "− tipRate", "− promoCode", telling the user the write had
54
+ * wiped four fields it never touched.
55
+ */
56
+ function patchDiff(before, patch) {
57
+ const a = rec(before) ?? {};
58
+ const b = rec(patch) ?? {};
59
+ const out = [];
60
+ for (const k of Object.keys(b)) {
61
+ if (typeof a[k] === "function" || typeof b[k] === "function") continue;
62
+ if (JSON.stringify(a[k]) !== JSON.stringify(b[k])) out.push({
63
+ path: k,
64
+ before: a[k],
65
+ after: b[k]
66
+ });
67
+ }
68
+ return out;
69
+ }
70
+
47
71
  /** Shallow diff of two plain objects: top-level keys that differ. */
48
72
  export function shallowDiff(before, after) {
49
73
  const a = rec(before) ?? {};
@@ -293,7 +317,17 @@ export function projectReceipt(input) {
293
317
  };
294
318
  }
295
319
  if (toolId === "zustand" && action === "setState" && r?.ok !== false) {
296
- const changes = shallowDiff(rec(input.before)?.currentState ?? input.before, params.state);
320
+ // Diff the store as it WAS against the store as it IS — never against the
321
+ // request. What was asked for and what landed are different things: a
322
+ // `path` edit carries no `state` at all (so the whole store read as
323
+ // deleted), a keyed list edit is expanded by the adapter before it is
324
+ // applied, and a merge leaves every omitted key alone. Reading both sides
325
+ // back is the only version of this that cannot lie, and it costs one local
326
+ // dispatch — no model tokens. `after` is absent only on a host that could
327
+ // not re-read the store; the patch-shaped fallbacks below are for that.
328
+ const beforeState = rec(input.before)?.currentState ?? input.before;
329
+ const afterState = input.after === undefined ? undefined : rec(input.after)?.currentState ?? input.after;
330
+ const changes = afterState !== undefined ? shallowDiff(beforeState, afterState) : params.replace === true ? shallowDiff(beforeState, params.state) : patchDiff(beforeState, params.state);
297
331
  if (changes.length === 0) return undefined;
298
332
  return {
299
333
  id: id(),
@@ -652,6 +686,28 @@ export function projectReceipt(input) {
652
686
  }
653
687
 
654
688
  // ---- react-query ----------------------------------------------------
689
+ if (toolId === "query" && action === "setQueryData" && r?.ok !== false) {
690
+ /**
691
+ * What the CACHE looks like now against what it looked like before —
692
+ * never against the request, for the same reasons as the zustand diff
693
+ * above: a `path` edit carries no `data` at all, a keyed list edit is
694
+ * expanded by the adapter before it lands, and a merge leaves every
695
+ * omitted key alone. Both sides are read back through `getQueryData`, so
696
+ * the card shows what actually moved.
697
+ */
698
+ const beforeData = rec(input.before)?.data ?? input.before;
699
+ const afterData = input.after === undefined ? undefined : rec(input.after)?.data ?? input.after;
700
+ const changes = afterData !== undefined ? shallowDiff(beforeData, afterData) : patchDiff(beforeData, params.data);
701
+ if (changes.length === 0) return undefined;
702
+ const key = Array.isArray(params.queryKey) ? params.queryKey.filter(k => typeof k === "string").join(" › ") : undefined;
703
+ return {
704
+ id: id(),
705
+ kind: "diff",
706
+ title: `${key || str(params.queryHash) || "query"} — what changed`,
707
+ changes,
708
+ source: "changed"
709
+ };
710
+ }
655
711
  if (toolId === "query" && action === "listQueries" && r) {
656
712
  const qs = arr(r.queries).map(rec).filter(Boolean);
657
713
  if (qs.length === 0) return undefined;