@cabane/companion 0.6.143 → 0.6.145
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 +133 -38
- package/dist/runtime.js +132 -37
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1816,14 +1816,11 @@ var init_turn_event = __esm({
|
|
|
1816
1816
|
// claude-code/codex it's the runtime's CUMULATIVE total summed across every model
|
|
1817
1817
|
// request in the agentic turn, so it grows with the tool-call count and is NOT
|
|
1818
1818
|
// "how full is the window." `contextTokens` is the distinct CONTEXT-OCCUPANCY
|
|
1819
|
-
// read
|
|
1820
|
-
//
|
|
1821
|
-
//
|
|
1822
|
-
// runtime
|
|
1823
|
-
//
|
|
1824
|
-
// clean final-request figure (codex's cumulative-only usage) omits `contextTokens`
|
|
1825
|
-
// and the gauge falls back to the raw count; `contextWindow` falls back to the
|
|
1826
|
-
// model catalog.
|
|
1819
|
+
// read and `contextWindow` the window it is measured against, both as the
|
|
1820
|
+
// harness reported them (claude-code's `getContextUsage`, or its final
|
|
1821
|
+
// request's input and `modelUsage` window when that call gives nothing). A
|
|
1822
|
+
// runtime that can't source them (codex's cumulative-only usage) omits both,
|
|
1823
|
+
// and no surface shows a reading for that turn.
|
|
1827
1824
|
//
|
|
1828
1825
|
// CT601: two more optional carry-homes on the terminal result, alongside
|
|
1829
1826
|
// `usage`. `resolvedModel` is the CONCRETE model the runtime actually ran —
|
|
@@ -1846,7 +1843,25 @@ var init_turn_event = __esm({
|
|
|
1846
1843
|
cacheReadTokens: z6.number().optional(),
|
|
1847
1844
|
cacheCreationTokens: z6.number().optional(),
|
|
1848
1845
|
contextTokens: z6.number().optional(),
|
|
1849
|
-
contextWindow: z6.number().optional()
|
|
1846
|
+
contextWindow: z6.number().optional(),
|
|
1847
|
+
// CT1751: the rest of the harness's own context reading, when it gave
|
|
1848
|
+
// one: where it will compact, its `/context` breakdown, and every
|
|
1849
|
+
// compaction it reported during the turn. Claude Code only.
|
|
1850
|
+
contextCompactAt: z6.number().optional(),
|
|
1851
|
+
contextCategories: z6.array(
|
|
1852
|
+
z6.object({
|
|
1853
|
+
name: z6.string(),
|
|
1854
|
+
tokens: z6.number(),
|
|
1855
|
+
kind: z6.enum(["used", "free", "buffer", "deferred"])
|
|
1856
|
+
})
|
|
1857
|
+
).optional(),
|
|
1858
|
+
contextCompactions: z6.array(
|
|
1859
|
+
z6.object({
|
|
1860
|
+
trigger: z6.enum(["manual", "auto"]),
|
|
1861
|
+
preTokens: z6.number(),
|
|
1862
|
+
postTokens: z6.number().optional()
|
|
1863
|
+
})
|
|
1864
|
+
).optional()
|
|
1850
1865
|
}).optional(),
|
|
1851
1866
|
resolvedModel: z6.string().optional(),
|
|
1852
1867
|
resolvedConfig: z6.object({
|
|
@@ -2244,24 +2259,18 @@ function summarizeToolInput(input) {
|
|
|
2244
2259
|
}
|
|
2245
2260
|
return "";
|
|
2246
2261
|
}
|
|
2247
|
-
function
|
|
2262
|
+
function renderToolIo(input) {
|
|
2248
2263
|
if (input === void 0 || input === null) return "";
|
|
2249
|
-
if (typeof input === "string")
|
|
2250
|
-
const trimmed = input.trim();
|
|
2251
|
-
return trimmed.length > 0 ? truncate(trimmed, TOOL_IO_MAX_CHARS) : "";
|
|
2252
|
-
}
|
|
2264
|
+
if (typeof input === "string") return input.trim();
|
|
2253
2265
|
try {
|
|
2254
|
-
return
|
|
2266
|
+
return JSON.stringify(input) ?? "";
|
|
2255
2267
|
} catch {
|
|
2256
2268
|
return "";
|
|
2257
2269
|
}
|
|
2258
2270
|
}
|
|
2259
|
-
function
|
|
2271
|
+
function renderToolResult(content) {
|
|
2260
2272
|
if (content === void 0 || content === null) return "";
|
|
2261
|
-
if (typeof content === "string")
|
|
2262
|
-
const trimmed = content.trim();
|
|
2263
|
-
return trimmed.length > 0 ? truncate(trimmed, TOOL_IO_MAX_CHARS) : "";
|
|
2264
|
-
}
|
|
2273
|
+
if (typeof content === "string") return content.trim();
|
|
2265
2274
|
if (Array.isArray(content)) {
|
|
2266
2275
|
const parts = [];
|
|
2267
2276
|
let sawContentEntry = false;
|
|
@@ -2276,12 +2285,12 @@ function summarizeToolResult(content) {
|
|
|
2276
2285
|
}
|
|
2277
2286
|
}
|
|
2278
2287
|
const joined = parts.join("\n").trim();
|
|
2279
|
-
if (joined.length > 0) return
|
|
2288
|
+
if (joined.length > 0) return joined;
|
|
2280
2289
|
if (sawContentEntry || content.length === 0) return "";
|
|
2281
2290
|
}
|
|
2282
2291
|
if (typeof content !== "object") return "";
|
|
2283
2292
|
if (!Array.isArray(content) && Object.keys(content).length === 0) return "";
|
|
2284
|
-
return
|
|
2293
|
+
return renderToolIo(content);
|
|
2285
2294
|
}
|
|
2286
2295
|
function truncate(s, max) {
|
|
2287
2296
|
if (s.length <= max) return s;
|
|
@@ -2305,14 +2314,19 @@ function sinkEmitter(sink, _onError) {
|
|
|
2305
2314
|
return;
|
|
2306
2315
|
case "tool":
|
|
2307
2316
|
if (sink.onToolActivity) {
|
|
2317
|
+
const input = renderToolIo(event.input);
|
|
2318
|
+
const result = event.result === void 0 ? null : renderToolResult(event.result);
|
|
2308
2319
|
await sink.onToolActivity({
|
|
2309
2320
|
toolUseId: event.id,
|
|
2310
2321
|
toolName: event.name,
|
|
2311
2322
|
summary: event.summary,
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2323
|
+
input: truncate(input, TOOL_IO_MAX_CHARS),
|
|
2324
|
+
result: result === null ? "" : truncate(result, TOOL_IO_MAX_CHARS),
|
|
2325
|
+
// CT1752: only when the event carried an input — a completion whose
|
|
2326
|
+
// start this process never saw has none, and a zero would overwrite
|
|
2327
|
+
// the size its start recorded.
|
|
2328
|
+
...event.input === void 0 ? {} : { inputChars: input.length },
|
|
2329
|
+
...result === null ? {} : { resultChars: result.length },
|
|
2316
2330
|
// CT496: carry the originating MCP server (null when absent / a host tool).
|
|
2317
2331
|
mcpServer: event.mcpServer ?? null,
|
|
2318
2332
|
phase: event.phase
|
|
@@ -2385,6 +2399,8 @@ var init_pump = __esm({
|
|
|
2385
2399
|
summary: evt.summary,
|
|
2386
2400
|
input: evt.input,
|
|
2387
2401
|
result: evt.result,
|
|
2402
|
+
...evt.inputChars !== void 0 ? { inputChars: evt.inputChars } : {},
|
|
2403
|
+
...evt.resultChars !== void 0 ? { resultChars: evt.resultChars } : {},
|
|
2388
2404
|
mcpServer: evt.mcpServer,
|
|
2389
2405
|
phase: evt.phase,
|
|
2390
2406
|
seq
|
|
@@ -2902,6 +2918,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
2902
2918
|
let resultReason;
|
|
2903
2919
|
let sawResult = false;
|
|
2904
2920
|
let usage;
|
|
2921
|
+
const compactions = [];
|
|
2905
2922
|
let lastRequestContextTokens;
|
|
2906
2923
|
let resolvedModel;
|
|
2907
2924
|
const signals = { sawRejectedLimit: false };
|
|
@@ -2927,6 +2944,9 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
2927
2944
|
...ctx.degraded ? { degraded: true } : {}
|
|
2928
2945
|
};
|
|
2929
2946
|
}
|
|
2947
|
+
} else if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
2948
|
+
const compaction = readCompaction(msg);
|
|
2949
|
+
if (compaction) compactions.push(compaction);
|
|
2930
2950
|
} else if (!workspaceProven && (msg.type === "assistant" || msg.type === "user" || msg.type === "result")) {
|
|
2931
2951
|
if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
|
|
2932
2952
|
const failure = classifyPreInitFrame(msg, signals);
|
|
@@ -2971,6 +2991,9 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
2971
2991
|
usage.contextTokens = lastRequestContextTokens;
|
|
2972
2992
|
const window = readContextWindow(msg, resolvedModel);
|
|
2973
2993
|
if (window !== void 0) usage.contextWindow = window;
|
|
2994
|
+
if (compactions.length > 0) usage.contextCompactions = compactions;
|
|
2995
|
+
const reading = await readHarnessContext(ctx.readContextUsage);
|
|
2996
|
+
if (reading) Object.assign(usage, reading);
|
|
2974
2997
|
}
|
|
2975
2998
|
const isError = msg.is_error === true;
|
|
2976
2999
|
if (msg.subtype === "success" && !isError) {
|
|
@@ -3121,6 +3144,56 @@ function readRequestContextTokens(msg) {
|
|
|
3121
3144
|
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3122
3145
|
return num(usage.input_tokens) + num(usage.cache_read_input_tokens) + num(usage.cache_creation_input_tokens);
|
|
3123
3146
|
}
|
|
3147
|
+
async function readHarnessContext(read) {
|
|
3148
|
+
if (!read) return void 0;
|
|
3149
|
+
let timer;
|
|
3150
|
+
const timeout = new Promise((resolve2) => {
|
|
3151
|
+
timer = setTimeout(() => resolve2(void 0), CONTEXT_READING_TIMEOUT_MS);
|
|
3152
|
+
});
|
|
3153
|
+
try {
|
|
3154
|
+
return parseContextUsage(await Promise.race([read(), timeout]));
|
|
3155
|
+
} catch {
|
|
3156
|
+
return void 0;
|
|
3157
|
+
} finally {
|
|
3158
|
+
clearTimeout(timer);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
function parseContextUsage(response) {
|
|
3162
|
+
const r = response;
|
|
3163
|
+
if (!r || typeof r !== "object") return void 0;
|
|
3164
|
+
const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
3165
|
+
const total = count(r.totalTokens);
|
|
3166
|
+
const window = count(r.maxTokens);
|
|
3167
|
+
if (total === void 0 || window === void 0 || window === 0) return void 0;
|
|
3168
|
+
const categories = [];
|
|
3169
|
+
for (const c of Array.isArray(r.categories) ? r.categories : []) {
|
|
3170
|
+
const row = c;
|
|
3171
|
+
const tokens = count(row?.tokens);
|
|
3172
|
+
if (typeof row?.name === "string" && tokens !== void 0 && typeof row.kind === "string" && CATEGORY_KINDS.has(row.kind)) {
|
|
3173
|
+
categories.push({ name: row.name, tokens, kind: row.kind });
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
const buffer = categories.filter((c) => c.kind === "buffer").reduce((sum, c) => sum + c.tokens, 0);
|
|
3177
|
+
return {
|
|
3178
|
+
contextTokens: total,
|
|
3179
|
+
contextWindow: window,
|
|
3180
|
+
...buffer > 0 && buffer < window ? { contextCompactAt: window - buffer } : {},
|
|
3181
|
+
...categories.length > 0 ? { contextCategories: categories } : {}
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
3184
|
+
function readCompaction(msg) {
|
|
3185
|
+
const meta = msg.compact_metadata;
|
|
3186
|
+
const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
3187
|
+
const preTokens = count(meta?.pre_tokens);
|
|
3188
|
+
if (!meta || preTokens === void 0) return void 0;
|
|
3189
|
+
if (meta.trigger !== "manual" && meta.trigger !== "auto") return void 0;
|
|
3190
|
+
const postTokens = count(meta.post_tokens);
|
|
3191
|
+
return {
|
|
3192
|
+
trigger: meta.trigger,
|
|
3193
|
+
preTokens,
|
|
3194
|
+
...postTokens !== void 0 ? { postTokens } : {}
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3124
3197
|
function readContextWindow(msg, resolvedModel) {
|
|
3125
3198
|
const modelUsage = msg.modelUsage;
|
|
3126
3199
|
if (!modelUsage || typeof modelUsage !== "object") return void 0;
|
|
@@ -3136,7 +3209,7 @@ function readContextWindow(msg, resolvedModel) {
|
|
|
3136
3209
|
}
|
|
3137
3210
|
return max;
|
|
3138
3211
|
}
|
|
3139
|
-
var HARNESS_PLACEHOLDERS;
|
|
3212
|
+
var HARNESS_PLACEHOLDERS, CONTEXT_READING_TIMEOUT_MS, CATEGORY_KINDS;
|
|
3140
3213
|
var init_loop = __esm({
|
|
3141
3214
|
"packages/agent-runtime/src/claude-code/loop.ts"() {
|
|
3142
3215
|
"use strict";
|
|
@@ -3144,11 +3217,13 @@ var init_loop = __esm({
|
|
|
3144
3217
|
init_failure();
|
|
3145
3218
|
init_session();
|
|
3146
3219
|
HARNESS_PLACEHOLDERS = /* @__PURE__ */ new Set(["No response requested.", "(no content)"]);
|
|
3220
|
+
CONTEXT_READING_TIMEOUT_MS = 2e3;
|
|
3221
|
+
CATEGORY_KINDS = /* @__PURE__ */ new Set(["used", "free", "buffer", "deferred"]);
|
|
3147
3222
|
}
|
|
3148
3223
|
});
|
|
3149
3224
|
|
|
3150
3225
|
// packages/agent-runtime/src/claude-code/prompt-input.ts
|
|
3151
|
-
function buildQueryPrompt(req) {
|
|
3226
|
+
function buildQueryPrompt(req, openUntil) {
|
|
3152
3227
|
const content = req.content;
|
|
3153
3228
|
return (async function* () {
|
|
3154
3229
|
yield {
|
|
@@ -3157,6 +3232,7 @@ function buildQueryPrompt(req) {
|
|
|
3157
3232
|
message: { role: "user", content },
|
|
3158
3233
|
parent_tool_use_id: null
|
|
3159
3234
|
};
|
|
3235
|
+
if (openUntil) await openUntil;
|
|
3160
3236
|
})();
|
|
3161
3237
|
}
|
|
3162
3238
|
var init_prompt_input = __esm({
|
|
@@ -3176,19 +3252,34 @@ function attachAbortController(options, signal) {
|
|
|
3176
3252
|
else signal.addEventListener("abort", () => sdkAbort.abort(), { once: true });
|
|
3177
3253
|
options.abortController = sdkAbort;
|
|
3178
3254
|
}
|
|
3179
|
-
function sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
|
|
3255
|
+
async function* sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
|
|
3180
3256
|
attachAbortController(built.options, signal);
|
|
3181
|
-
|
|
3182
|
-
const iter = {
|
|
3183
|
-
[Symbol.asyncIterator]: () => queryFn({ prompt, options: built.options })[Symbol.asyncIterator]()
|
|
3257
|
+
let release = () => {
|
|
3184
3258
|
};
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
cwd: req.local.cwd,
|
|
3188
|
-
resumedSessionId: built.resume,
|
|
3189
|
-
degraded,
|
|
3190
|
-
...startEvidence ? { startEvidence } : {}
|
|
3259
|
+
const held = new Promise((resolve2) => {
|
|
3260
|
+
release = resolve2;
|
|
3191
3261
|
});
|
|
3262
|
+
const prompt = buildQueryPrompt(req, held);
|
|
3263
|
+
let active;
|
|
3264
|
+
const iter = {
|
|
3265
|
+
[Symbol.asyncIterator]: () => {
|
|
3266
|
+
const q = queryFn({ prompt, options: built.options });
|
|
3267
|
+
active = q;
|
|
3268
|
+
return q[Symbol.asyncIterator]();
|
|
3269
|
+
}
|
|
3270
|
+
};
|
|
3271
|
+
try {
|
|
3272
|
+
yield* decodeSdkStream(iter, {
|
|
3273
|
+
signal,
|
|
3274
|
+
readContextUsage: async () => typeof active?.getContextUsage === "function" ? active.getContextUsage({ detail: "summary" }) : void 0,
|
|
3275
|
+
cwd: req.local.cwd,
|
|
3276
|
+
resumedSessionId: built.resume,
|
|
3277
|
+
degraded,
|
|
3278
|
+
...startEvidence ? { startEvidence } : {}
|
|
3279
|
+
});
|
|
3280
|
+
} finally {
|
|
3281
|
+
release();
|
|
3282
|
+
}
|
|
3192
3283
|
}
|
|
3193
3284
|
async function freshRequest(req, reason, hooks) {
|
|
3194
3285
|
const recomposed = hooks?.fresh ? await hooks.fresh(reason) : null;
|
|
@@ -8883,6 +8974,8 @@ var init_turn_committer = __esm({
|
|
|
8883
8974
|
summary,
|
|
8884
8975
|
input,
|
|
8885
8976
|
result,
|
|
8977
|
+
inputChars,
|
|
8978
|
+
resultChars,
|
|
8886
8979
|
mcpServer,
|
|
8887
8980
|
phase,
|
|
8888
8981
|
seq
|
|
@@ -8898,6 +8991,8 @@ var init_turn_committer = __esm({
|
|
|
8898
8991
|
summary,
|
|
8899
8992
|
input,
|
|
8900
8993
|
result,
|
|
8994
|
+
...inputChars !== void 0 ? { inputChars } : {},
|
|
8995
|
+
...resultChars !== void 0 ? { resultChars } : {},
|
|
8901
8996
|
mcpServer,
|
|
8902
8997
|
phase,
|
|
8903
8998
|
seq,
|
package/dist/runtime.js
CHANGED
|
@@ -3309,14 +3309,11 @@ var turnEventSchema = z6.discriminatedUnion("type", [
|
|
|
3309
3309
|
// claude-code/codex it's the runtime's CUMULATIVE total summed across every model
|
|
3310
3310
|
// request in the agentic turn, so it grows with the tool-call count and is NOT
|
|
3311
3311
|
// "how full is the window." `contextTokens` is the distinct CONTEXT-OCCUPANCY
|
|
3312
|
-
// read
|
|
3313
|
-
//
|
|
3314
|
-
//
|
|
3315
|
-
// runtime
|
|
3316
|
-
//
|
|
3317
|
-
// clean final-request figure (codex's cumulative-only usage) omits `contextTokens`
|
|
3318
|
-
// and the gauge falls back to the raw count; `contextWindow` falls back to the
|
|
3319
|
-
// model catalog.
|
|
3312
|
+
// read and `contextWindow` the window it is measured against, both as the
|
|
3313
|
+
// harness reported them (claude-code's `getContextUsage`, or its final
|
|
3314
|
+
// request's input and `modelUsage` window when that call gives nothing). A
|
|
3315
|
+
// runtime that can't source them (codex's cumulative-only usage) omits both,
|
|
3316
|
+
// and no surface shows a reading for that turn.
|
|
3320
3317
|
//
|
|
3321
3318
|
// CT601: two more optional carry-homes on the terminal result, alongside
|
|
3322
3319
|
// `usage`. `resolvedModel` is the CONCRETE model the runtime actually ran —
|
|
@@ -3339,7 +3336,25 @@ var turnEventSchema = z6.discriminatedUnion("type", [
|
|
|
3339
3336
|
cacheReadTokens: z6.number().optional(),
|
|
3340
3337
|
cacheCreationTokens: z6.number().optional(),
|
|
3341
3338
|
contextTokens: z6.number().optional(),
|
|
3342
|
-
contextWindow: z6.number().optional()
|
|
3339
|
+
contextWindow: z6.number().optional(),
|
|
3340
|
+
// CT1751: the rest of the harness's own context reading, when it gave
|
|
3341
|
+
// one: where it will compact, its `/context` breakdown, and every
|
|
3342
|
+
// compaction it reported during the turn. Claude Code only.
|
|
3343
|
+
contextCompactAt: z6.number().optional(),
|
|
3344
|
+
contextCategories: z6.array(
|
|
3345
|
+
z6.object({
|
|
3346
|
+
name: z6.string(),
|
|
3347
|
+
tokens: z6.number(),
|
|
3348
|
+
kind: z6.enum(["used", "free", "buffer", "deferred"])
|
|
3349
|
+
})
|
|
3350
|
+
).optional(),
|
|
3351
|
+
contextCompactions: z6.array(
|
|
3352
|
+
z6.object({
|
|
3353
|
+
trigger: z6.enum(["manual", "auto"]),
|
|
3354
|
+
preTokens: z6.number(),
|
|
3355
|
+
postTokens: z6.number().optional()
|
|
3356
|
+
})
|
|
3357
|
+
).optional()
|
|
3343
3358
|
}).optional(),
|
|
3344
3359
|
resolvedModel: z6.string().optional(),
|
|
3345
3360
|
resolvedConfig: z6.object({
|
|
@@ -3704,24 +3719,18 @@ function summarizeToolInput(input) {
|
|
|
3704
3719
|
return "";
|
|
3705
3720
|
}
|
|
3706
3721
|
var TOOL_IO_MAX_CHARS = 1e4;
|
|
3707
|
-
function
|
|
3722
|
+
function renderToolIo(input) {
|
|
3708
3723
|
if (input === void 0 || input === null) return "";
|
|
3709
|
-
if (typeof input === "string")
|
|
3710
|
-
const trimmed = input.trim();
|
|
3711
|
-
return trimmed.length > 0 ? truncate(trimmed, TOOL_IO_MAX_CHARS) : "";
|
|
3712
|
-
}
|
|
3724
|
+
if (typeof input === "string") return input.trim();
|
|
3713
3725
|
try {
|
|
3714
|
-
return
|
|
3726
|
+
return JSON.stringify(input) ?? "";
|
|
3715
3727
|
} catch {
|
|
3716
3728
|
return "";
|
|
3717
3729
|
}
|
|
3718
3730
|
}
|
|
3719
|
-
function
|
|
3731
|
+
function renderToolResult(content) {
|
|
3720
3732
|
if (content === void 0 || content === null) return "";
|
|
3721
|
-
if (typeof content === "string")
|
|
3722
|
-
const trimmed = content.trim();
|
|
3723
|
-
return trimmed.length > 0 ? truncate(trimmed, TOOL_IO_MAX_CHARS) : "";
|
|
3724
|
-
}
|
|
3733
|
+
if (typeof content === "string") return content.trim();
|
|
3725
3734
|
if (Array.isArray(content)) {
|
|
3726
3735
|
const parts = [];
|
|
3727
3736
|
let sawContentEntry = false;
|
|
@@ -3736,12 +3745,12 @@ function summarizeToolResult(content) {
|
|
|
3736
3745
|
}
|
|
3737
3746
|
}
|
|
3738
3747
|
const joined = parts.join("\n").trim();
|
|
3739
|
-
if (joined.length > 0) return
|
|
3748
|
+
if (joined.length > 0) return joined;
|
|
3740
3749
|
if (sawContentEntry || content.length === 0) return "";
|
|
3741
3750
|
}
|
|
3742
3751
|
if (typeof content !== "object") return "";
|
|
3743
3752
|
if (!Array.isArray(content) && Object.keys(content).length === 0) return "";
|
|
3744
|
-
return
|
|
3753
|
+
return renderToolIo(content);
|
|
3745
3754
|
}
|
|
3746
3755
|
function truncate(s, max) {
|
|
3747
3756
|
if (s.length <= max) return s;
|
|
@@ -3758,14 +3767,19 @@ function sinkEmitter(sink, _onError) {
|
|
|
3758
3767
|
return;
|
|
3759
3768
|
case "tool":
|
|
3760
3769
|
if (sink.onToolActivity) {
|
|
3770
|
+
const input = renderToolIo(event.input);
|
|
3771
|
+
const result = event.result === void 0 ? null : renderToolResult(event.result);
|
|
3761
3772
|
await sink.onToolActivity({
|
|
3762
3773
|
toolUseId: event.id,
|
|
3763
3774
|
toolName: event.name,
|
|
3764
3775
|
summary: event.summary,
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3776
|
+
input: truncate(input, TOOL_IO_MAX_CHARS),
|
|
3777
|
+
result: result === null ? "" : truncate(result, TOOL_IO_MAX_CHARS),
|
|
3778
|
+
// CT1752: only when the event carried an input — a completion whose
|
|
3779
|
+
// start this process never saw has none, and a zero would overwrite
|
|
3780
|
+
// the size its start recorded.
|
|
3781
|
+
...event.input === void 0 ? {} : { inputChars: input.length },
|
|
3782
|
+
...result === null ? {} : { resultChars: result.length },
|
|
3769
3783
|
// CT496: carry the originating MCP server (null when absent / a host tool).
|
|
3770
3784
|
mcpServer: event.mcpServer ?? null,
|
|
3771
3785
|
phase: event.phase
|
|
@@ -3826,6 +3840,8 @@ var TurnPump = class {
|
|
|
3826
3840
|
summary: evt.summary,
|
|
3827
3841
|
input: evt.input,
|
|
3828
3842
|
result: evt.result,
|
|
3843
|
+
...evt.inputChars !== void 0 ? { inputChars: evt.inputChars } : {},
|
|
3844
|
+
...evt.resultChars !== void 0 ? { resultChars: evt.resultChars } : {},
|
|
3829
3845
|
mcpServer: evt.mcpServer,
|
|
3830
3846
|
phase: evt.phase,
|
|
3831
3847
|
seq
|
|
@@ -4294,6 +4310,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4294
4310
|
let resultReason;
|
|
4295
4311
|
let sawResult = false;
|
|
4296
4312
|
let usage;
|
|
4313
|
+
const compactions = [];
|
|
4297
4314
|
let lastRequestContextTokens;
|
|
4298
4315
|
let resolvedModel;
|
|
4299
4316
|
const signals = { sawRejectedLimit: false };
|
|
@@ -4319,6 +4336,9 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4319
4336
|
...ctx.degraded ? { degraded: true } : {}
|
|
4320
4337
|
};
|
|
4321
4338
|
}
|
|
4339
|
+
} else if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
4340
|
+
const compaction = readCompaction(msg);
|
|
4341
|
+
if (compaction) compactions.push(compaction);
|
|
4322
4342
|
} else if (!workspaceProven && (msg.type === "assistant" || msg.type === "user" || msg.type === "result")) {
|
|
4323
4343
|
if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
|
|
4324
4344
|
const failure = classifyPreInitFrame(msg, signals);
|
|
@@ -4363,6 +4383,9 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4363
4383
|
usage.contextTokens = lastRequestContextTokens;
|
|
4364
4384
|
const window = readContextWindow(msg, resolvedModel);
|
|
4365
4385
|
if (window !== void 0) usage.contextWindow = window;
|
|
4386
|
+
if (compactions.length > 0) usage.contextCompactions = compactions;
|
|
4387
|
+
const reading = await readHarnessContext(ctx.readContextUsage);
|
|
4388
|
+
if (reading) Object.assign(usage, reading);
|
|
4366
4389
|
}
|
|
4367
4390
|
const isError = msg.is_error === true;
|
|
4368
4391
|
if (msg.subtype === "success" && !isError) {
|
|
@@ -4514,6 +4537,58 @@ function readRequestContextTokens(msg) {
|
|
|
4514
4537
|
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
4515
4538
|
return num(usage.input_tokens) + num(usage.cache_read_input_tokens) + num(usage.cache_creation_input_tokens);
|
|
4516
4539
|
}
|
|
4540
|
+
var CONTEXT_READING_TIMEOUT_MS = 2e3;
|
|
4541
|
+
async function readHarnessContext(read) {
|
|
4542
|
+
if (!read) return void 0;
|
|
4543
|
+
let timer;
|
|
4544
|
+
const timeout = new Promise((resolve2) => {
|
|
4545
|
+
timer = setTimeout(() => resolve2(void 0), CONTEXT_READING_TIMEOUT_MS);
|
|
4546
|
+
});
|
|
4547
|
+
try {
|
|
4548
|
+
return parseContextUsage(await Promise.race([read(), timeout]));
|
|
4549
|
+
} catch {
|
|
4550
|
+
return void 0;
|
|
4551
|
+
} finally {
|
|
4552
|
+
clearTimeout(timer);
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
var CATEGORY_KINDS = /* @__PURE__ */ new Set(["used", "free", "buffer", "deferred"]);
|
|
4556
|
+
function parseContextUsage(response) {
|
|
4557
|
+
const r = response;
|
|
4558
|
+
if (!r || typeof r !== "object") return void 0;
|
|
4559
|
+
const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
4560
|
+
const total = count(r.totalTokens);
|
|
4561
|
+
const window = count(r.maxTokens);
|
|
4562
|
+
if (total === void 0 || window === void 0 || window === 0) return void 0;
|
|
4563
|
+
const categories = [];
|
|
4564
|
+
for (const c of Array.isArray(r.categories) ? r.categories : []) {
|
|
4565
|
+
const row = c;
|
|
4566
|
+
const tokens = count(row?.tokens);
|
|
4567
|
+
if (typeof row?.name === "string" && tokens !== void 0 && typeof row.kind === "string" && CATEGORY_KINDS.has(row.kind)) {
|
|
4568
|
+
categories.push({ name: row.name, tokens, kind: row.kind });
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
const buffer = categories.filter((c) => c.kind === "buffer").reduce((sum, c) => sum + c.tokens, 0);
|
|
4572
|
+
return {
|
|
4573
|
+
contextTokens: total,
|
|
4574
|
+
contextWindow: window,
|
|
4575
|
+
...buffer > 0 && buffer < window ? { contextCompactAt: window - buffer } : {},
|
|
4576
|
+
...categories.length > 0 ? { contextCategories: categories } : {}
|
|
4577
|
+
};
|
|
4578
|
+
}
|
|
4579
|
+
function readCompaction(msg) {
|
|
4580
|
+
const meta = msg.compact_metadata;
|
|
4581
|
+
const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
4582
|
+
const preTokens = count(meta?.pre_tokens);
|
|
4583
|
+
if (!meta || preTokens === void 0) return void 0;
|
|
4584
|
+
if (meta.trigger !== "manual" && meta.trigger !== "auto") return void 0;
|
|
4585
|
+
const postTokens = count(meta.post_tokens);
|
|
4586
|
+
return {
|
|
4587
|
+
trigger: meta.trigger,
|
|
4588
|
+
preTokens,
|
|
4589
|
+
...postTokens !== void 0 ? { postTokens } : {}
|
|
4590
|
+
};
|
|
4591
|
+
}
|
|
4517
4592
|
function readContextWindow(msg, resolvedModel) {
|
|
4518
4593
|
const modelUsage = msg.modelUsage;
|
|
4519
4594
|
if (!modelUsage || typeof modelUsage !== "object") return void 0;
|
|
@@ -4531,7 +4606,7 @@ function readContextWindow(msg, resolvedModel) {
|
|
|
4531
4606
|
}
|
|
4532
4607
|
|
|
4533
4608
|
// packages/agent-runtime/src/claude-code/prompt-input.ts
|
|
4534
|
-
function buildQueryPrompt(req) {
|
|
4609
|
+
function buildQueryPrompt(req, openUntil) {
|
|
4535
4610
|
const content = req.content;
|
|
4536
4611
|
return (async function* () {
|
|
4537
4612
|
yield {
|
|
@@ -4540,6 +4615,7 @@ function buildQueryPrompt(req) {
|
|
|
4540
4615
|
message: { role: "user", content },
|
|
4541
4616
|
parent_tool_use_id: null
|
|
4542
4617
|
};
|
|
4618
|
+
if (openUntil) await openUntil;
|
|
4543
4619
|
})();
|
|
4544
4620
|
}
|
|
4545
4621
|
|
|
@@ -4551,19 +4627,34 @@ function attachAbortController(options, signal) {
|
|
|
4551
4627
|
else signal.addEventListener("abort", () => sdkAbort.abort(), { once: true });
|
|
4552
4628
|
options.abortController = sdkAbort;
|
|
4553
4629
|
}
|
|
4554
|
-
function sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
|
|
4630
|
+
async function* sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
|
|
4555
4631
|
attachAbortController(built.options, signal);
|
|
4556
|
-
|
|
4557
|
-
const iter = {
|
|
4558
|
-
[Symbol.asyncIterator]: () => queryFn({ prompt, options: built.options })[Symbol.asyncIterator]()
|
|
4632
|
+
let release = () => {
|
|
4559
4633
|
};
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
cwd: req.local.cwd,
|
|
4563
|
-
resumedSessionId: built.resume,
|
|
4564
|
-
degraded,
|
|
4565
|
-
...startEvidence ? { startEvidence } : {}
|
|
4634
|
+
const held = new Promise((resolve2) => {
|
|
4635
|
+
release = resolve2;
|
|
4566
4636
|
});
|
|
4637
|
+
const prompt = buildQueryPrompt(req, held);
|
|
4638
|
+
let active;
|
|
4639
|
+
const iter = {
|
|
4640
|
+
[Symbol.asyncIterator]: () => {
|
|
4641
|
+
const q = queryFn({ prompt, options: built.options });
|
|
4642
|
+
active = q;
|
|
4643
|
+
return q[Symbol.asyncIterator]();
|
|
4644
|
+
}
|
|
4645
|
+
};
|
|
4646
|
+
try {
|
|
4647
|
+
yield* decodeSdkStream(iter, {
|
|
4648
|
+
signal,
|
|
4649
|
+
readContextUsage: async () => typeof active?.getContextUsage === "function" ? active.getContextUsage({ detail: "summary" }) : void 0,
|
|
4650
|
+
cwd: req.local.cwd,
|
|
4651
|
+
resumedSessionId: built.resume,
|
|
4652
|
+
degraded,
|
|
4653
|
+
...startEvidence ? { startEvidence } : {}
|
|
4654
|
+
});
|
|
4655
|
+
} finally {
|
|
4656
|
+
release();
|
|
4657
|
+
}
|
|
4567
4658
|
}
|
|
4568
4659
|
async function freshRequest(req, reason, hooks) {
|
|
4569
4660
|
const recomposed = hooks?.fresh ? await hooks.fresh(reason) : null;
|
|
@@ -9902,6 +9993,8 @@ var TurnCommitter = class {
|
|
|
9902
9993
|
summary,
|
|
9903
9994
|
input,
|
|
9904
9995
|
result,
|
|
9996
|
+
inputChars,
|
|
9997
|
+
resultChars,
|
|
9905
9998
|
mcpServer,
|
|
9906
9999
|
phase,
|
|
9907
10000
|
seq
|
|
@@ -9917,6 +10010,8 @@ var TurnCommitter = class {
|
|
|
9917
10010
|
summary,
|
|
9918
10011
|
input,
|
|
9919
10012
|
result,
|
|
10013
|
+
...inputChars !== void 0 ? { inputChars } : {},
|
|
10014
|
+
...resultChars !== void 0 ? { resultChars } : {},
|
|
9920
10015
|
mcpServer,
|
|
9921
10016
|
phase,
|
|
9922
10017
|
seq,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.145",
|
|
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",
|