@cabane/companion 0.6.74 → 0.6.75
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/cli.js +118 -6
- package/dist/runtime.js +118 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3048,6 +3048,12 @@ var turnEventSchema = z5.discriminatedUnion("type", [
|
|
|
3048
3048
|
// `progress` row); `terminal: true` is the turn's closing reply (commits as
|
|
3049
3049
|
// the `final` row).
|
|
3050
3050
|
z5.object({ type: z5.literal("text"), body: z5.string(), terminal: z5.boolean() }),
|
|
3051
|
+
// Runtime/provider-authored prose surfaced alongside a failed turn. Unlike
|
|
3052
|
+
// `text`, this is not the agent's narration or reply: the pump persists it as
|
|
3053
|
+
// `runtime_notice`, and the transcript renders it in the shared SystemNote
|
|
3054
|
+
// voice. Adapters emit it only from positive runtime evidence; the web never
|
|
3055
|
+
// classifies English strings.
|
|
3056
|
+
z5.object({ type: z5.literal("runtime_notice"), body: z5.string() }),
|
|
3051
3057
|
// A tool-activity transition. Maps `onToolActivity` — `toolUseId`→`id`,
|
|
3052
3058
|
// `toolName`→`name` (already prefix-stripped: `cabane_read`, not
|
|
3053
3059
|
// `mcp__cabane__cabane_read`), `summary` is the short card label. `phase`
|
|
@@ -3181,6 +3187,7 @@ var turnDiagnosticsSchema = z6.object({
|
|
|
3181
3187
|
eventCounts: z6.object({
|
|
3182
3188
|
session: z6.number().int().nonnegative(),
|
|
3183
3189
|
text: z6.number().int().nonnegative(),
|
|
3190
|
+
runtime_notice: z6.number().int().nonnegative(),
|
|
3184
3191
|
thinking: z6.number().int().nonnegative(),
|
|
3185
3192
|
tool: z6.number().int().nonnegative(),
|
|
3186
3193
|
result: z6.number().int().nonnegative()
|
|
@@ -3279,6 +3286,17 @@ function classifyErrorText(text) {
|
|
|
3279
3286
|
if (BARE_LIMIT.test(t)) return { kind: "usage_capped" };
|
|
3280
3287
|
return null;
|
|
3281
3288
|
}
|
|
3289
|
+
function classifyRuntimeNoticeText(text) {
|
|
3290
|
+
if (!text) return null;
|
|
3291
|
+
const normalized = text.trim().replace(/\s+/g, " ");
|
|
3292
|
+
if (!normalized) return null;
|
|
3293
|
+
if (/^you(?:'|’)ve hit your (?:session|weekly|usage) limit(?:\s*[·—-]\s*resets?\s+.+)?$/i.test(
|
|
3294
|
+
normalized
|
|
3295
|
+
)) {
|
|
3296
|
+
return { kind: "usage_capped" };
|
|
3297
|
+
}
|
|
3298
|
+
return null;
|
|
3299
|
+
}
|
|
3282
3300
|
var AUTH_PATTERNS = [
|
|
3283
3301
|
/authentication[_ ]error/,
|
|
3284
3302
|
/invalid[_ ]?(x-)?api[_ ]?key/,
|
|
@@ -3346,6 +3364,8 @@ function isContentBearingEvent(event) {
|
|
|
3346
3364
|
return normalizeCommitText(event.body) !== "";
|
|
3347
3365
|
case "thinking":
|
|
3348
3366
|
return normalizeCommitText(event.text) !== "";
|
|
3367
|
+
case "runtime_notice":
|
|
3368
|
+
return normalizeCommitText(event.body) !== "";
|
|
3349
3369
|
case "tool":
|
|
3350
3370
|
return true;
|
|
3351
3371
|
case "session":
|
|
@@ -3542,6 +3562,12 @@ async function flushHeldText(buffer, emit, terminal, onError) {
|
|
|
3542
3562
|
buffer.held = null;
|
|
3543
3563
|
await safeEmit(emit, { type: "text", body, terminal }, onError);
|
|
3544
3564
|
}
|
|
3565
|
+
async function flushHeldRuntimeNotice(buffer, emit, onError) {
|
|
3566
|
+
if (buffer.held === null) return;
|
|
3567
|
+
const body = buffer.held;
|
|
3568
|
+
buffer.held = null;
|
|
3569
|
+
await safeEmit(emit, { type: "runtime_notice", body }, onError);
|
|
3570
|
+
}
|
|
3545
3571
|
function extractAssistantBlocks(msg) {
|
|
3546
3572
|
const texts = [];
|
|
3547
3573
|
const toolUses = [];
|
|
@@ -3663,6 +3689,9 @@ function sinkEmitter(sink, _onError) {
|
|
|
3663
3689
|
case "thinking":
|
|
3664
3690
|
if (sink.onThinking) await sink.onThinking({ text: event.text });
|
|
3665
3691
|
return;
|
|
3692
|
+
case "runtime_notice":
|
|
3693
|
+
if (sink.onRuntimeNotice) await sink.onRuntimeNotice({ text: event.body });
|
|
3694
|
+
return;
|
|
3666
3695
|
// `session` / `result` are host/pump territory — never produced by the
|
|
3667
3696
|
// transcript classification, so they have no callback to render onto.
|
|
3668
3697
|
case "session":
|
|
@@ -3716,6 +3745,13 @@ var TurnPump = class {
|
|
|
3716
3745
|
const text = truncate(trimmed, THINKING_TEXT_MAX_CHARS);
|
|
3717
3746
|
const seq = opts.nextSeq();
|
|
3718
3747
|
await opts.commit.reportThinking({ text, seq });
|
|
3748
|
+
},
|
|
3749
|
+
onRuntimeNotice: async (evt) => {
|
|
3750
|
+
if (opts.signal.aborted) return;
|
|
3751
|
+
const body = normalizeCommitText(evt.text);
|
|
3752
|
+
if (!body) return;
|
|
3753
|
+
const seq = opts.nextSeq();
|
|
3754
|
+
await opts.commit.commitMessage({ body, kind: "runtime_notice", seq });
|
|
3719
3755
|
}
|
|
3720
3756
|
};
|
|
3721
3757
|
}
|
|
@@ -4125,7 +4161,13 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4125
4161
|
ok = false;
|
|
4126
4162
|
resultReason ??= "session_start_failed";
|
|
4127
4163
|
}
|
|
4128
|
-
|
|
4164
|
+
const heldFailure = !ok ? classifyRuntimeNoticeText(buffer.held) : null;
|
|
4165
|
+
const terminalFailure = !ok ? decodeFailureReason(resultReason) : null;
|
|
4166
|
+
if (heldFailure && terminalFailure && heldFailure.kind === terminalFailure.kind) {
|
|
4167
|
+
await flushHeldRuntimeNotice(buffer, emit);
|
|
4168
|
+
} else {
|
|
4169
|
+
await flushHeldText(buffer, emit, ok);
|
|
4170
|
+
}
|
|
4129
4171
|
yield* drain(out);
|
|
4130
4172
|
if (!ok && !resultReason && !sawResult) resultReason = "no_result";
|
|
4131
4173
|
yield {
|
|
@@ -4647,22 +4689,92 @@ var BASE_CLAUDE_CODE_CONFORMANCE_FIXTURES = [
|
|
|
4647
4689
|
{
|
|
4648
4690
|
// CT558/CT592/CT731: a subscription cap. The SDK emits a rejected
|
|
4649
4691
|
// `rate_limit_event` with a named subscription window; the terminal error result
|
|
4650
|
-
// then classifies as the structured `usage_capped` reason.
|
|
4651
|
-
//
|
|
4692
|
+
// then classifies as the structured `usage_capped` reason. The held provider
|
|
4693
|
+
// line independently classifies to the same kind, so it lands as a runtime
|
|
4694
|
+
// notice rather than agent-authored progress.
|
|
4652
4695
|
name: "subscription cap \u2192 usage_capped",
|
|
4653
4696
|
request: makeRequest(),
|
|
4654
4697
|
nativeStream: [
|
|
4655
4698
|
init("s1"),
|
|
4656
|
-
assistantText("
|
|
4699
|
+
assistantText("You've hit your session limit \xB7 resets 8:20pm (UTC)"),
|
|
4700
|
+
rateLimitEvent("rejected", void 0, "five_hour"),
|
|
4701
|
+
resultError("error_during_execution")
|
|
4702
|
+
],
|
|
4703
|
+
expected: [
|
|
4704
|
+
sessionEvent("s1"),
|
|
4705
|
+
{
|
|
4706
|
+
type: "runtime_notice",
|
|
4707
|
+
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
|
|
4708
|
+
},
|
|
4709
|
+
{ type: "result", ok: false, reason: "usage_capped" }
|
|
4710
|
+
]
|
|
4711
|
+
},
|
|
4712
|
+
{
|
|
4713
|
+
// CT1336: a failed turn can still contain the agent's own partial narration.
|
|
4714
|
+
// No text classification means no authorship rewrite, even though the
|
|
4715
|
+
// structured result is the same usage cap.
|
|
4716
|
+
name: "partial narration before subscription cap stays progress",
|
|
4717
|
+
request: makeRequest(),
|
|
4718
|
+
nativeStream: [
|
|
4719
|
+
init("s1"),
|
|
4720
|
+
assistantText("Partial work."),
|
|
4657
4721
|
rateLimitEvent("rejected", void 0, "five_hour"),
|
|
4658
4722
|
resultError("error_during_execution")
|
|
4659
4723
|
],
|
|
4660
4724
|
expected: [
|
|
4661
4725
|
sessionEvent("s1"),
|
|
4662
|
-
{ type: "text", body: "
|
|
4726
|
+
{ type: "text", body: "Partial work.", terminal: false },
|
|
4663
4727
|
{ type: "result", ok: false, reason: "usage_capped" }
|
|
4664
4728
|
]
|
|
4665
4729
|
},
|
|
4730
|
+
{
|
|
4731
|
+
// CT1336: even recognized runtime prose stays authored when its kind
|
|
4732
|
+
// disagrees with the structured terminal failure.
|
|
4733
|
+
name: "cap notice before server failure stays progress",
|
|
4734
|
+
request: makeRequest(),
|
|
4735
|
+
nativeStream: [
|
|
4736
|
+
init("s1"),
|
|
4737
|
+
assistantText("You've hit your session limit \xB7 resets 8:20pm (UTC)"),
|
|
4738
|
+
resultErrorFull("error_during_execution", {
|
|
4739
|
+
is_error: true,
|
|
4740
|
+
result: '529 {"type":"error","error":{"type":"overloaded_error"}}'
|
|
4741
|
+
})
|
|
4742
|
+
],
|
|
4743
|
+
expected: [
|
|
4744
|
+
sessionEvent("s1"),
|
|
4745
|
+
{
|
|
4746
|
+
type: "text",
|
|
4747
|
+
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)",
|
|
4748
|
+
terminal: false
|
|
4749
|
+
},
|
|
4750
|
+
{ type: "result", ok: false, reason: "server_error" }
|
|
4751
|
+
]
|
|
4752
|
+
},
|
|
4753
|
+
{
|
|
4754
|
+
// CT1336 review: `classifyErrorText` intentionally searches arbitrary error
|
|
4755
|
+
// blobs for status tokens. Assistant narration needs the stricter whole-
|
|
4756
|
+
// notice classifier, or an incidental count is de-authored when the turn
|
|
4757
|
+
// later fails with the matching provider kind.
|
|
4758
|
+
name: "incidental 5xx-shaped narration before server failure stays progress",
|
|
4759
|
+
request: makeRequest(),
|
|
4760
|
+
nativeStream: [
|
|
4761
|
+
init("s1"),
|
|
4762
|
+
assistantText("Indexed 500 files. Writing the summary next."),
|
|
4763
|
+
resultErrorFull("error_during_execution", {
|
|
4764
|
+
is_error: true,
|
|
4765
|
+
result: '529 {"type":"error","error":{"type":"overloaded_error"}}'
|
|
4766
|
+
})
|
|
4767
|
+
],
|
|
4768
|
+
expected: [
|
|
4769
|
+
sessionEvent("s1"),
|
|
4770
|
+
{
|
|
4771
|
+
type: "text",
|
|
4772
|
+
body: "Indexed 500 files. Writing the summary next.",
|
|
4773
|
+
terminal: false
|
|
4774
|
+
},
|
|
4775
|
+
{ type: "result", ok: false, reason: "server_error" }
|
|
4776
|
+
]
|
|
4777
|
+
},
|
|
4666
4778
|
{
|
|
4667
4779
|
// CT558/CT592: a cap signalled only by the result's
|
|
4668
4780
|
// `terminal_reason:'blocking_limit'` (no discrete rate-limit event) still
|
|
@@ -8149,7 +8261,7 @@ function initialOutcome() {
|
|
|
8149
8261
|
turnResolvedModel: void 0,
|
|
8150
8262
|
turnResolvedConfig: void 0,
|
|
8151
8263
|
turnMcpInventory: void 0,
|
|
8152
|
-
eventCounts: { session: 0, text: 0, thinking: 0, tool: 0, result: 0 },
|
|
8264
|
+
eventCounts: { session: 0, text: 0, runtime_notice: 0, thinking: 0, tool: 0, result: 0 },
|
|
8153
8265
|
runtimeResultKind: null,
|
|
8154
8266
|
contentBearingEvents: 0,
|
|
8155
8267
|
latestSessionState: null,
|
package/dist/runtime.js
CHANGED
|
@@ -2547,6 +2547,12 @@ var turnEventSchema = z5.discriminatedUnion("type", [
|
|
|
2547
2547
|
// `progress` row); `terminal: true` is the turn's closing reply (commits as
|
|
2548
2548
|
// the `final` row).
|
|
2549
2549
|
z5.object({ type: z5.literal("text"), body: z5.string(), terminal: z5.boolean() }),
|
|
2550
|
+
// Runtime/provider-authored prose surfaced alongside a failed turn. Unlike
|
|
2551
|
+
// `text`, this is not the agent's narration or reply: the pump persists it as
|
|
2552
|
+
// `runtime_notice`, and the transcript renders it in the shared SystemNote
|
|
2553
|
+
// voice. Adapters emit it only from positive runtime evidence; the web never
|
|
2554
|
+
// classifies English strings.
|
|
2555
|
+
z5.object({ type: z5.literal("runtime_notice"), body: z5.string() }),
|
|
2550
2556
|
// A tool-activity transition. Maps `onToolActivity` — `toolUseId`→`id`,
|
|
2551
2557
|
// `toolName`→`name` (already prefix-stripped: `cabane_read`, not
|
|
2552
2558
|
// `mcp__cabane__cabane_read`), `summary` is the short card label. `phase`
|
|
@@ -2680,6 +2686,7 @@ var turnDiagnosticsSchema = z6.object({
|
|
|
2680
2686
|
eventCounts: z6.object({
|
|
2681
2687
|
session: z6.number().int().nonnegative(),
|
|
2682
2688
|
text: z6.number().int().nonnegative(),
|
|
2689
|
+
runtime_notice: z6.number().int().nonnegative(),
|
|
2683
2690
|
thinking: z6.number().int().nonnegative(),
|
|
2684
2691
|
tool: z6.number().int().nonnegative(),
|
|
2685
2692
|
result: z6.number().int().nonnegative()
|
|
@@ -2778,6 +2785,17 @@ function classifyErrorText(text) {
|
|
|
2778
2785
|
if (BARE_LIMIT.test(t)) return { kind: "usage_capped" };
|
|
2779
2786
|
return null;
|
|
2780
2787
|
}
|
|
2788
|
+
function classifyRuntimeNoticeText(text) {
|
|
2789
|
+
if (!text) return null;
|
|
2790
|
+
const normalized = text.trim().replace(/\s+/g, " ");
|
|
2791
|
+
if (!normalized) return null;
|
|
2792
|
+
if (/^you(?:'|’)ve hit your (?:session|weekly|usage) limit(?:\s*[·—-]\s*resets?\s+.+)?$/i.test(
|
|
2793
|
+
normalized
|
|
2794
|
+
)) {
|
|
2795
|
+
return { kind: "usage_capped" };
|
|
2796
|
+
}
|
|
2797
|
+
return null;
|
|
2798
|
+
}
|
|
2781
2799
|
var AUTH_PATTERNS = [
|
|
2782
2800
|
/authentication[_ ]error/,
|
|
2783
2801
|
/invalid[_ ]?(x-)?api[_ ]?key/,
|
|
@@ -2845,6 +2863,8 @@ function isContentBearingEvent(event) {
|
|
|
2845
2863
|
return normalizeCommitText(event.body) !== "";
|
|
2846
2864
|
case "thinking":
|
|
2847
2865
|
return normalizeCommitText(event.text) !== "";
|
|
2866
|
+
case "runtime_notice":
|
|
2867
|
+
return normalizeCommitText(event.body) !== "";
|
|
2848
2868
|
case "tool":
|
|
2849
2869
|
return true;
|
|
2850
2870
|
case "session":
|
|
@@ -3041,6 +3061,12 @@ async function flushHeldText(buffer, emit, terminal, onError) {
|
|
|
3041
3061
|
buffer.held = null;
|
|
3042
3062
|
await safeEmit(emit, { type: "text", body, terminal }, onError);
|
|
3043
3063
|
}
|
|
3064
|
+
async function flushHeldRuntimeNotice(buffer, emit, onError) {
|
|
3065
|
+
if (buffer.held === null) return;
|
|
3066
|
+
const body = buffer.held;
|
|
3067
|
+
buffer.held = null;
|
|
3068
|
+
await safeEmit(emit, { type: "runtime_notice", body }, onError);
|
|
3069
|
+
}
|
|
3044
3070
|
function extractAssistantBlocks(msg) {
|
|
3045
3071
|
const texts = [];
|
|
3046
3072
|
const toolUses = [];
|
|
@@ -3162,6 +3188,9 @@ function sinkEmitter(sink, _onError) {
|
|
|
3162
3188
|
case "thinking":
|
|
3163
3189
|
if (sink.onThinking) await sink.onThinking({ text: event.text });
|
|
3164
3190
|
return;
|
|
3191
|
+
case "runtime_notice":
|
|
3192
|
+
if (sink.onRuntimeNotice) await sink.onRuntimeNotice({ text: event.body });
|
|
3193
|
+
return;
|
|
3165
3194
|
// `session` / `result` are host/pump territory — never produced by the
|
|
3166
3195
|
// transcript classification, so they have no callback to render onto.
|
|
3167
3196
|
case "session":
|
|
@@ -3215,6 +3244,13 @@ var TurnPump = class {
|
|
|
3215
3244
|
const text = truncate(trimmed, THINKING_TEXT_MAX_CHARS);
|
|
3216
3245
|
const seq = opts.nextSeq();
|
|
3217
3246
|
await opts.commit.reportThinking({ text, seq });
|
|
3247
|
+
},
|
|
3248
|
+
onRuntimeNotice: async (evt) => {
|
|
3249
|
+
if (opts.signal.aborted) return;
|
|
3250
|
+
const body = normalizeCommitText(evt.text);
|
|
3251
|
+
if (!body) return;
|
|
3252
|
+
const seq = opts.nextSeq();
|
|
3253
|
+
await opts.commit.commitMessage({ body, kind: "runtime_notice", seq });
|
|
3218
3254
|
}
|
|
3219
3255
|
};
|
|
3220
3256
|
}
|
|
@@ -3624,7 +3660,13 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
3624
3660
|
ok = false;
|
|
3625
3661
|
resultReason ??= "session_start_failed";
|
|
3626
3662
|
}
|
|
3627
|
-
|
|
3663
|
+
const heldFailure = !ok ? classifyRuntimeNoticeText(buffer.held) : null;
|
|
3664
|
+
const terminalFailure = !ok ? decodeFailureReason(resultReason) : null;
|
|
3665
|
+
if (heldFailure && terminalFailure && heldFailure.kind === terminalFailure.kind) {
|
|
3666
|
+
await flushHeldRuntimeNotice(buffer, emit);
|
|
3667
|
+
} else {
|
|
3668
|
+
await flushHeldText(buffer, emit, ok);
|
|
3669
|
+
}
|
|
3628
3670
|
yield* drain(out);
|
|
3629
3671
|
if (!ok && !resultReason && !sawResult) resultReason = "no_result";
|
|
3630
3672
|
yield {
|
|
@@ -4146,22 +4188,92 @@ var BASE_CLAUDE_CODE_CONFORMANCE_FIXTURES = [
|
|
|
4146
4188
|
{
|
|
4147
4189
|
// CT558/CT592/CT731: a subscription cap. The SDK emits a rejected
|
|
4148
4190
|
// `rate_limit_event` with a named subscription window; the terminal error result
|
|
4149
|
-
// then classifies as the structured `usage_capped` reason.
|
|
4150
|
-
//
|
|
4191
|
+
// then classifies as the structured `usage_capped` reason. The held provider
|
|
4192
|
+
// line independently classifies to the same kind, so it lands as a runtime
|
|
4193
|
+
// notice rather than agent-authored progress.
|
|
4151
4194
|
name: "subscription cap \u2192 usage_capped",
|
|
4152
4195
|
request: makeRequest(),
|
|
4153
4196
|
nativeStream: [
|
|
4154
4197
|
init("s1"),
|
|
4155
|
-
assistantText("
|
|
4198
|
+
assistantText("You've hit your session limit \xB7 resets 8:20pm (UTC)"),
|
|
4199
|
+
rateLimitEvent("rejected", void 0, "five_hour"),
|
|
4200
|
+
resultError("error_during_execution")
|
|
4201
|
+
],
|
|
4202
|
+
expected: [
|
|
4203
|
+
sessionEvent("s1"),
|
|
4204
|
+
{
|
|
4205
|
+
type: "runtime_notice",
|
|
4206
|
+
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)"
|
|
4207
|
+
},
|
|
4208
|
+
{ type: "result", ok: false, reason: "usage_capped" }
|
|
4209
|
+
]
|
|
4210
|
+
},
|
|
4211
|
+
{
|
|
4212
|
+
// CT1336: a failed turn can still contain the agent's own partial narration.
|
|
4213
|
+
// No text classification means no authorship rewrite, even though the
|
|
4214
|
+
// structured result is the same usage cap.
|
|
4215
|
+
name: "partial narration before subscription cap stays progress",
|
|
4216
|
+
request: makeRequest(),
|
|
4217
|
+
nativeStream: [
|
|
4218
|
+
init("s1"),
|
|
4219
|
+
assistantText("Partial work."),
|
|
4156
4220
|
rateLimitEvent("rejected", void 0, "five_hour"),
|
|
4157
4221
|
resultError("error_during_execution")
|
|
4158
4222
|
],
|
|
4159
4223
|
expected: [
|
|
4160
4224
|
sessionEvent("s1"),
|
|
4161
|
-
{ type: "text", body: "
|
|
4225
|
+
{ type: "text", body: "Partial work.", terminal: false },
|
|
4162
4226
|
{ type: "result", ok: false, reason: "usage_capped" }
|
|
4163
4227
|
]
|
|
4164
4228
|
},
|
|
4229
|
+
{
|
|
4230
|
+
// CT1336: even recognized runtime prose stays authored when its kind
|
|
4231
|
+
// disagrees with the structured terminal failure.
|
|
4232
|
+
name: "cap notice before server failure stays progress",
|
|
4233
|
+
request: makeRequest(),
|
|
4234
|
+
nativeStream: [
|
|
4235
|
+
init("s1"),
|
|
4236
|
+
assistantText("You've hit your session limit \xB7 resets 8:20pm (UTC)"),
|
|
4237
|
+
resultErrorFull("error_during_execution", {
|
|
4238
|
+
is_error: true,
|
|
4239
|
+
result: '529 {"type":"error","error":{"type":"overloaded_error"}}'
|
|
4240
|
+
})
|
|
4241
|
+
],
|
|
4242
|
+
expected: [
|
|
4243
|
+
sessionEvent("s1"),
|
|
4244
|
+
{
|
|
4245
|
+
type: "text",
|
|
4246
|
+
body: "You've hit your session limit \xB7 resets 8:20pm (UTC)",
|
|
4247
|
+
terminal: false
|
|
4248
|
+
},
|
|
4249
|
+
{ type: "result", ok: false, reason: "server_error" }
|
|
4250
|
+
]
|
|
4251
|
+
},
|
|
4252
|
+
{
|
|
4253
|
+
// CT1336 review: `classifyErrorText` intentionally searches arbitrary error
|
|
4254
|
+
// blobs for status tokens. Assistant narration needs the stricter whole-
|
|
4255
|
+
// notice classifier, or an incidental count is de-authored when the turn
|
|
4256
|
+
// later fails with the matching provider kind.
|
|
4257
|
+
name: "incidental 5xx-shaped narration before server failure stays progress",
|
|
4258
|
+
request: makeRequest(),
|
|
4259
|
+
nativeStream: [
|
|
4260
|
+
init("s1"),
|
|
4261
|
+
assistantText("Indexed 500 files. Writing the summary next."),
|
|
4262
|
+
resultErrorFull("error_during_execution", {
|
|
4263
|
+
is_error: true,
|
|
4264
|
+
result: '529 {"type":"error","error":{"type":"overloaded_error"}}'
|
|
4265
|
+
})
|
|
4266
|
+
],
|
|
4267
|
+
expected: [
|
|
4268
|
+
sessionEvent("s1"),
|
|
4269
|
+
{
|
|
4270
|
+
type: "text",
|
|
4271
|
+
body: "Indexed 500 files. Writing the summary next.",
|
|
4272
|
+
terminal: false
|
|
4273
|
+
},
|
|
4274
|
+
{ type: "result", ok: false, reason: "server_error" }
|
|
4275
|
+
]
|
|
4276
|
+
},
|
|
4165
4277
|
{
|
|
4166
4278
|
// CT558/CT592: a cap signalled only by the result's
|
|
4167
4279
|
// `terminal_reason:'blocking_limit'` (no discrete rate-limit event) still
|
|
@@ -7648,7 +7760,7 @@ function initialOutcome() {
|
|
|
7648
7760
|
turnResolvedModel: void 0,
|
|
7649
7761
|
turnResolvedConfig: void 0,
|
|
7650
7762
|
turnMcpInventory: void 0,
|
|
7651
|
-
eventCounts: { session: 0, text: 0, thinking: 0, tool: 0, result: 0 },
|
|
7763
|
+
eventCounts: { session: 0, text: 0, runtime_notice: 0, thinking: 0, tool: 0, result: 0 },
|
|
7652
7764
|
runtimeResultKind: null,
|
|
7653
7765
|
contentBearingEvents: 0,
|
|
7654
7766
|
latestSessionState: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.75",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect a coding agent on your machine to your Cabane workspace as a responder — drive work against your own codebase, files, and MCP servers without putting any of it in Cabane.",
|
|
6
6
|
"license": "UNLICENSED",
|