@code-yeongyu/senpi-ai 2026.10.26-1 → 2026.10.26-2

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.
@@ -123,6 +123,7 @@ export function assistantEvents(source, onProgress) {
123
123
  }) ?? source[Symbol.asyncIterator](),
124
124
  };
125
125
  }
126
+ /** Internal readers address a block by `capture.contentIndex ?? event.contentIndex`; the public index can drift (Google/Vertex). */
126
127
  export function getAssistantCapture(event) {
127
128
  return captures.get(event);
128
129
  }
@@ -178,28 +179,44 @@ export function carryAssistantCapture(source, target, terminal) {
178
179
  if (!capture)
179
180
  return target;
180
181
  try {
181
- captures.set(target, terminal
182
- ? Object.freeze({
183
- ...capture,
184
- message: freezeCapture({ ...terminal, content: terminal.content.map(publicBlock) }),
185
- })
186
- : capture);
182
+ captures.set(target, terminal ? terminalCapture(capture, terminal) : capture);
187
183
  }
188
184
  catch (error) {
189
185
  dropCapture(source, error);
190
186
  }
191
187
  return target;
192
188
  }
189
+ /** A terminal replaced downstream: its usage and message, plus every scalar that differs from the source terminal. */
190
+ function terminalCapture(capture, terminal) {
191
+ const metadata = { ...capture.metadata };
192
+ const previous = capture.message ?? {};
193
+ for (const key of new Set([...Object.keys(previous), ...Object.keys(terminal)])) {
194
+ if (key === "content" || key === "usage")
195
+ continue;
196
+ const value = Reflect.get(terminal, key);
197
+ if (!sameValue(value, Reflect.get(previous, key)))
198
+ metadata[key] = key in terminal ? value : null;
199
+ }
200
+ return Object.freeze({
201
+ usage: frozenUsage(terminal.usage),
202
+ metadata: freezeCapture(metadata),
203
+ message: freezeCapture({ ...terminal, content: terminal.content.map(publicBlock) }),
204
+ ...(capture.rewrittenFrom === undefined ? {} : { rewrittenFrom: capture.rewrittenFrom }),
205
+ });
206
+ }
193
207
  /** Never parses or traverses a block on a delta, including a no-growth delta. */
194
208
  export function captureAssistantEvent(owner, event, hint = {}) {
195
209
  const state = activeState(owner);
196
- if (!state || captures.has(event))
210
+ // A relay of a source that captured nothing, or captured a drifted index, gets no record: lack, never wrong.
211
+ if (!state || captures.has(event) || hint.carried === null || hint.carried?.contentIndex !== undefined)
197
212
  return;
198
213
  const contentIndex = "contentIndex" in event ? (hint.contentIndex ?? event.contentIndex) : undefined;
199
214
  if (contentIndex !== undefined && state.failedBlocks.has(contentIndex))
200
215
  return;
201
216
  try {
202
- captures.set(event, buildCapture(state, event, hint));
217
+ const capture = buildCapture(state, event, hint);
218
+ if (capture)
219
+ captures.set(event, capture);
203
220
  }
204
221
  catch (error) {
205
222
  dropCapture(owner, error, state, contentIndex);
@@ -213,11 +230,21 @@ function buildCapture(state, event, hint) {
213
230
  usage,
214
231
  metadata: changedMetadata(state, message),
215
232
  message: freezeCapture({ ...message, content: message.content.map(publicBlock) }),
233
+ ...(hint.rewrittenFrom === undefined ? {} : { rewrittenFrom: hint.rewrittenFrom }),
216
234
  });
217
235
  }
218
236
  const index = hint.contentIndex ?? event.contentIndex;
219
237
  const own = index === event.contentIndex ? {} : { contentIndex: index };
238
+ const carried = hint.carried;
220
239
  if ("delta" in event) {
240
+ if (carried) {
241
+ const { parsedThrough } = carried;
242
+ return Object.freeze({
243
+ usage,
244
+ offset: carried.offset,
245
+ ...(parsedThrough === undefined ? {} : { parsedThrough }),
246
+ });
247
+ }
221
248
  const position = state.positions.get(index) ?? { offset: 0, parsedThrough: 0 };
222
249
  const offset = hint.offset ?? position.offset;
223
250
  position.offset = offset + event.delta.length;
@@ -233,10 +260,18 @@ function buildCapture(state, event, hint) {
233
260
  });
234
261
  }
235
262
  const metadata = changedMetadata(state, message);
236
- const block = freezeCapture(publicBlock(event.type === "toolcall_end" ? event.toolCall : message.content[index]));
237
- if (event.type !== "text_start" && event.type !== "thinking_start" && event.type !== "toolcall_start") {
238
- return Object.freeze({ usage, metadata, block, ...own });
263
+ if (carried) {
264
+ const { usage: _usage, metadata: _metadata, message: _message, offset: _offset, ...facts } = carried;
265
+ return Object.freeze({ ...facts, usage, metadata });
239
266
  }
267
+ const starts = event.type === "text_start" || event.type === "thinking_start" || event.type === "toolcall_start";
268
+ const source = event.type === "toolcall_end" ? event.toolCall : message.content[index];
269
+ // A start announced before its block is appended has no seed to freeze yet.
270
+ if (starts && source === undefined)
271
+ return undefined;
272
+ const block = freezeCapture(publicBlock(source));
273
+ if (!starts)
274
+ return Object.freeze({ usage, metadata, block, ...own });
240
275
  const rawArgumentPrefix = hint.rawArgumentPrefix ?? "";
241
276
  const coveredCodeUnits = block.type === "text"
242
277
  ? block.text.length
@@ -257,13 +292,15 @@ function buildCapture(state, event, hint) {
257
292
  function usageCopy(state, usage) {
258
293
  if (state.usage !== undefined && sameValue(usage, state.usage))
259
294
  return state.usage;
260
- const copy = Object.freeze({
295
+ state.usage = frozenUsage(usage);
296
+ return state.usage;
297
+ }
298
+ function frozenUsage(usage) {
299
+ return Object.freeze({
261
300
  ...usage,
262
301
  cost: Object.freeze({ ...usage.cost }),
263
302
  ...(usage.contextUsage ? { contextUsage: Object.freeze({ ...usage.contextUsage }) } : {}),
264
303
  });
265
- state.usage = copy;
266
- return copy;
267
304
  }
268
305
  /** Scalar message fields changed since the last non-delta capture; the baseline moves only once its copy exists. */
269
306
  function changedMetadata(state, message) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-ai",
3
- "version": "2026.10.26-1",
3
+ "version": "2026.10.26-2",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -88,7 +88,7 @@
88
88
  "dependencies": {
89
89
  "@anthropic-ai/sdk": "0.127.0",
90
90
  "@aws-sdk/client-bedrock-runtime": "3.1136.0",
91
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.26-1",
91
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.26-2",
92
92
  "@google/genai": "2.23.0",
93
93
  "@smithy/node-http-handler": "4.12.1",
94
94
  "google-auth-library": "10.6.2",