@vincemakes/kiso-core 0.40.0 → 0.40.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.
package/dist/errors.js CHANGED
@@ -28,8 +28,8 @@ export function parseRetryAfter(value, now = Date.now()) {
28
28
  }
29
29
  /** ADR-0055 §3: how the providers word a refused context — openai-compat's
30
30
  * "maximum context length" / `context_length_exceeded`, Anthropic's
31
- * "prompt is too long". That 400 is an overflow, not a malformed request:
32
- * the kernel compacts once and retries once. */
31
+ * "prompt is too long". It is an overflow, not a malformed request, at any
32
+ * status (Amendment 2): the kernel compacts once and retries once. */
33
33
  const CONTEXT_TOO_LONG = /maximum context length|context_length_exceeded|prompt is too long|context window/i;
34
34
  export function mapApiError(status, message, retryAfterMs) {
35
35
  const withStatus = (e) => ({
@@ -37,6 +37,11 @@ export function mapApiError(status, message, retryAfterMs) {
37
37
  ...(status !== undefined ? { status } : {}),
38
38
  ...(retryAfterMs !== undefined && Number.isFinite(retryAfterMs) && retryAfterMs >= 0 ? { retryAfterMs } : {}),
39
39
  });
40
+ // ADR-0055 Amendment 2 (decision 3): the refusal is recognised by what it
41
+ // SAYS, whatever the status — a streamed 400 arrives with none (d4fc), and
42
+ // a 5xx that says so would only re-send what cannot fit.
43
+ if (CONTEXT_TOO_LONG.test(message))
44
+ return withStatus({ code: "context_overflow", retryable: false, message });
40
45
  switch (status) {
41
46
  case 401:
42
47
  case 403:
@@ -48,7 +53,7 @@ export function mapApiError(status, message, retryAfterMs) {
48
53
  case 529:
49
54
  return withStatus({ code: "overloaded", retryable: true, message });
50
55
  case 400:
51
- return withStatus({ code: CONTEXT_TOO_LONG.test(message) ? "context_overflow" : "invalid_request", retryable: false, message });
56
+ return withStatus({ code: "invalid_request", retryable: false, message });
52
57
  default:
53
58
  if (status !== undefined && status >= 500 && status < 600) {
54
59
  // D4: every 500-599 is api_5xx and retryable.
@@ -143,23 +143,21 @@ export function projectMessages(events) {
143
143
  if (ev.type === "tool_call_end")
144
144
  callMeta.set(ev.callId, { name: ev.name, input: ev.input });
145
145
  }
146
- // ADR-0044: summarized coverage — each `summarized` event covers the
147
- // range (previous coversToSeq, coversToSeq] of ORDINARY events. The
148
- // ranges are disjoint and in seq order; boundaries are turn boundaries
149
- // by construction (summaryBoundarySeq cuts before a user_input), so a
150
- // skipped event never splits a message. Each summary message renders
151
- // AT ITS BOUNDARY — the first event after the covered range — NOT at
152
- // the summarized event itself: the event sits at the log's END (the
153
- // kept rounds live between the boundary and it), and the summary must
154
- // precede the kept conversation in reading order.
146
+ // ADR-0055 Amendment 2: a checkpoint REPLACES every earlier one. The
147
+ // summarized event with the greatest coversToSeq covers (−1,
148
+ // coversToSeq] of ORDINARY events and is the ONE summary that renders;
149
+ // the superseded ones stay durable and render nothing. (ADR-0044's
150
+ // disjoint ranges kept every earlier summary beside an in-band
151
+ // checkpoint that already restated them — the 0.40.1 stacking.) The
152
+ // boundary is a turn boundary by construction, so a skipped event never
153
+ // splits a message. The summary renders AT ITS BOUNDARY — the first
154
+ // event after the covered range — NOT at the summarized event itself:
155
+ // the event sits at the log's END (the kept rounds live between the
156
+ // boundary and it), and the summary must precede the kept conversation.
155
157
  const summaryRanges = [];
156
- {
157
- let prev = -1;
158
- for (const ev of events) {
159
- if (ev.type === "summarized") {
160
- summaryRanges.push({ from: prev, to: ev.coversToSeq, summary: ev.summary });
161
- prev = ev.coversToSeq;
162
- }
158
+ for (const ev of events) {
159
+ if (ev.type === "summarized" && (summaryRanges[0] === undefined || ev.coversToSeq >= summaryRanges[0].to)) {
160
+ summaryRanges[0] = { from: -1, to: ev.coversToSeq, summary: ev.summary };
163
161
  }
164
162
  }
165
163
  // R-E 0.1.43 (Gap B): a model_output_abandoned marker voids the range
@@ -365,15 +365,16 @@ export interface MicroCompactEvent {
365
365
  }
366
366
  /**
367
367
  * ADR-0044 — a model-generated summary replaced the covered conversation
368
- * range. `coversToSeq` is the seq of the LAST covered event; the covered
369
- * range runs from just past the previous `summarized` event's coversToSeq
370
- * (or the trajectory's start for the first) up to coversToSeq. The
371
- * projection replaces exactly those events with ONE USER message carrying
372
- * the summary behind a fixed framing line (E6's boundary honesty: the
373
- * model reads compressed history as CONTEXT, never as a reply it produced
374
- * — see SUMMARY_FRAMING in kernel/project.ts). It is not an assistant
375
- * message; every `summarized` event always renders its own message. Byte-stable: a summarized event is a persisted fact, so the
376
- * same events derive the same messages on every replay.
368
+ * range. `coversToSeq` is the seq of the LAST covered event. ADR-0055
369
+ * Amendment 2: a checkpoint REPLACES every earlier one — the covered range
370
+ * is (−1, coversToSeq], ranges nest, and only the summarized event with the
371
+ * greatest coversToSeq renders; superseded ones stay durable and render
372
+ * nothing. The projection replaces the covered events with ONE USER
373
+ * message carrying the summary behind a fixed framing line (E6's boundary
374
+ * honesty: the model reads compressed history as CONTEXT, never as a reply
375
+ * it produced — see SUMMARY_FRAMING in kernel/project.ts). Byte-stable: a
376
+ * summarized event is a persisted fact, so the same events derive the
377
+ * same messages on every replay.
377
378
  *
378
379
  * The summary is generated OFF-LOOP through the session's own adapter —
379
380
  * the summary request itself never enters the log, and a failed summary
@@ -383,7 +384,7 @@ export interface MicroCompactEvent {
383
384
  export interface SummarizedEvent {
384
385
  readonly seq: number;
385
386
  readonly type: "summarized";
386
- /** The last covered event's seq; the range is (previous coversToSeq, coversToSeq]. */
387
+ /** The last covered event's seq; the range is (−1, coversToSeq] — the latest checkpoint replaces the earlier ones. */
387
388
  readonly coversToSeq: number;
388
389
  /** The model's compression — replaces the covered range in the projection. */
389
390
  readonly summary: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-core",
3
- "version": "0.40.0",
3
+ "version": "0.40.2",
4
4
  "description": "kiso core \u2014 protocol, event log, loop, hooks, modes, permissions, compaction, delivery truth. The 2,000-line kernel at the bottom of the kiso framework.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,7 +33,7 @@
33
33
  "openai"
34
34
  ],
35
35
  "devDependencies": {
36
- "@vincemakes/kiso-evals": "0.40.0",
36
+ "@vincemakes/kiso-evals": "0.40.2",
37
37
  "@types/node": "^26.1.2",
38
38
  "typescript": "^5.7.2",
39
39
  "vitest": "^3.0.0"