@cabane/companion 0.6.140 → 0.6.142
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 +45 -9
- package/dist/runtime.js +44 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1763,7 +1763,8 @@ var init_turn_event = __esm({
|
|
|
1763
1763
|
// `progress` row); `terminal: true` is the turn's closing reply (commits as
|
|
1764
1764
|
// the `final` row).
|
|
1765
1765
|
z6.object({ type: z6.literal("text"), body: z6.string(), terminal: z6.boolean() }),
|
|
1766
|
-
// Runtime/provider-authored prose
|
|
1766
|
+
// Runtime/provider-authored prose about the turn — a failure's explanation, or
|
|
1767
|
+
// a line the harness wrote on a turn it recovered (CT1750). Unlike
|
|
1767
1768
|
// `text`, this is not the agent's narration or reply: the pump persists it as
|
|
1768
1769
|
// `runtime_notice`, and the transcript renders it in the shared SystemNote
|
|
1769
1770
|
// voice. Adapters emit it only from positive runtime evidence; the web never
|
|
@@ -1971,10 +1972,12 @@ function isContentBearingEvent(event) {
|
|
|
1971
1972
|
return normalizeCommitText(event.body) !== "";
|
|
1972
1973
|
case "thinking":
|
|
1973
1974
|
return normalizeCommitText(event.text) !== "";
|
|
1974
|
-
case "runtime_notice":
|
|
1975
|
-
return normalizeCommitText(event.body) !== "";
|
|
1976
1975
|
case "tool":
|
|
1977
1976
|
return true;
|
|
1977
|
+
// CT1750: a runtime notice is shown, but it is the harness speaking about
|
|
1978
|
+
// the turn, not the turn's output — a success whose only output is a CLI
|
|
1979
|
+
// line still produced nothing.
|
|
1980
|
+
case "runtime_notice":
|
|
1978
1981
|
case "session":
|
|
1979
1982
|
case "result":
|
|
1980
1983
|
return false;
|
|
@@ -2561,8 +2564,7 @@ var init_addendum = __esm({
|
|
|
2561
2564
|
CLAUDE_CODE_ADDENDUM = [
|
|
2562
2565
|
"On this harness the Cabane tools register under MCP prefixes. The workspace tools are",
|
|
2563
2566
|
"`mcp__cabane__query`, `mcp__cabane__mutate` and `mcp__cabane__describe`; the ancillary",
|
|
2564
|
-
"tools are `
|
|
2565
|
-
"`mcp__cabane__read_binary`, `mcp__cabane__upload`, `mcp__cabane__begin_upload`,",
|
|
2567
|
+
"tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`, `mcp__cabane__begin_upload`,",
|
|
2566
2568
|
"`mcp__cabane__finalize_upload`, and `mcp__cabane__mint_render_token`.",
|
|
2567
2569
|
turnControlPromptGuidance("mcp__cabane_companion__"),
|
|
2568
2570
|
"Where this prompt names a tool by its bare verb, call",
|
|
@@ -2903,6 +2905,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
2903
2905
|
let lastRequestContextTokens;
|
|
2904
2906
|
let resolvedModel;
|
|
2905
2907
|
const signals = { sawRejectedLimit: false };
|
|
2908
|
+
let preInitNotice = null;
|
|
2906
2909
|
try {
|
|
2907
2910
|
for await (const msg of iter) {
|
|
2908
2911
|
if (ctx.signal.aborted) return;
|
|
@@ -2928,12 +2931,19 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
2928
2931
|
if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
|
|
2929
2932
|
const failure = classifyPreInitFrame(msg, signals);
|
|
2930
2933
|
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
2934
|
+
if (msg.type === "assistant" && isHarnessAuthored(msg))
|
|
2935
|
+
preInitNotice = harnessNoticeText(msg);
|
|
2931
2936
|
break;
|
|
2932
2937
|
} else if (msg.type === "assistant") {
|
|
2933
2938
|
const assistantErr = msg.error;
|
|
2934
2939
|
if (typeof assistantErr === "string" && assistantErr.length > 0) {
|
|
2935
2940
|
signals.lastAssistantError = assistantErr;
|
|
2936
2941
|
}
|
|
2942
|
+
if (isHarnessAuthored(msg)) {
|
|
2943
|
+
await emitHarnessMessage(msg, emit, pending, buffer);
|
|
2944
|
+
yield* drain(out);
|
|
2945
|
+
continue;
|
|
2946
|
+
}
|
|
2937
2947
|
const reqContext = readRequestContextTokens(msg);
|
|
2938
2948
|
if (reqContext !== void 0) lastRequestContextTokens = reqContext;
|
|
2939
2949
|
await processAssistantMessage(msg, emit, pending, buffer);
|
|
@@ -2995,6 +3005,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
2995
3005
|
const failure = classifyFailure(signals, { text: "" });
|
|
2996
3006
|
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
2997
3007
|
}
|
|
3008
|
+
if (preInitNotice) yield { type: "runtime_notice", body: preInitNotice };
|
|
2998
3009
|
}
|
|
2999
3010
|
const heldFailure = !ok ? classifyRuntimeNoticeText(buffer.held) : null;
|
|
3000
3011
|
const terminalFailure = !ok ? decodeFailureReason(resultReason) : null;
|
|
@@ -3061,6 +3072,29 @@ function preInitToolActivity(msg) {
|
|
|
3061
3072
|
const kind = msg.type === "assistant" ? "tool_use" : msg.type === "user" ? "tool_result" : null;
|
|
3062
3073
|
return kind !== null && contentBlocks(msg).some((b) => b.type === kind);
|
|
3063
3074
|
}
|
|
3075
|
+
function isHarnessAuthored(msg) {
|
|
3076
|
+
const error = msg.error;
|
|
3077
|
+
if (typeof error === "string" && error.length > 0) return true;
|
|
3078
|
+
return msg.message?.model === "<synthetic>";
|
|
3079
|
+
}
|
|
3080
|
+
function harnessNoticeText(msg) {
|
|
3081
|
+
const body = contentBlocks(msg).flatMap((b) => b.type === "text" && typeof b.text === "string" ? [b.text] : []).join("\n\n").trim();
|
|
3082
|
+
return body && !HARNESS_PLACEHOLDERS.has(body) ? body : null;
|
|
3083
|
+
}
|
|
3084
|
+
async function emitHarnessMessage(msg, emit, pending, buffer) {
|
|
3085
|
+
const body = harnessNoticeText(msg);
|
|
3086
|
+
if (body) emit({ type: "runtime_notice", body });
|
|
3087
|
+
const toolUses = contentBlocks(msg).filter((b) => b.type === "tool_use");
|
|
3088
|
+
if (toolUses.length > 0) {
|
|
3089
|
+
const inner = msg.message ?? {};
|
|
3090
|
+
await processAssistantMessage(
|
|
3091
|
+
{ message: { ...inner, content: toolUses } },
|
|
3092
|
+
emit,
|
|
3093
|
+
pending,
|
|
3094
|
+
buffer
|
|
3095
|
+
);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3064
3098
|
function contentBlocks(msg) {
|
|
3065
3099
|
const content = msg.message?.content;
|
|
3066
3100
|
return Array.isArray(content) ? content : [];
|
|
@@ -3102,12 +3136,14 @@ function readContextWindow(msg, resolvedModel) {
|
|
|
3102
3136
|
}
|
|
3103
3137
|
return max;
|
|
3104
3138
|
}
|
|
3139
|
+
var HARNESS_PLACEHOLDERS;
|
|
3105
3140
|
var init_loop = __esm({
|
|
3106
3141
|
"packages/agent-runtime/src/claude-code/loop.ts"() {
|
|
3107
3142
|
"use strict";
|
|
3108
3143
|
init_transcript();
|
|
3109
3144
|
init_failure();
|
|
3110
3145
|
init_session();
|
|
3146
|
+
HARNESS_PLACEHOLDERS = /* @__PURE__ */ new Set(["No response requested.", "(no content)"]);
|
|
3111
3147
|
}
|
|
3112
3148
|
});
|
|
3113
3149
|
|
|
@@ -3180,7 +3216,7 @@ async function* runWithResumeRecovery(queryFn, req, signal, initial, augmentOpti
|
|
|
3180
3216
|
yield* sdkEvents(queryFn, fresh.req, signal, options, fresh.degraded);
|
|
3181
3217
|
return;
|
|
3182
3218
|
}
|
|
3183
|
-
emitted = true;
|
|
3219
|
+
if (event.type !== "runtime_notice") emitted = true;
|
|
3184
3220
|
yield event;
|
|
3185
3221
|
}
|
|
3186
3222
|
}
|
|
@@ -3853,7 +3889,7 @@ var init_addendum2 = __esm({
|
|
|
3853
3889
|
"Every tool here goes by its plain name: the workspace tools are `query`, `mutate` and",
|
|
3854
3890
|
"`describe`.",
|
|
3855
3891
|
turnControlPromptGuidance(""),
|
|
3856
|
-
"The ancillary tools are `
|
|
3892
|
+
"The ancillary tools are `read_binary`, `upload`, `begin_upload`,",
|
|
3857
3893
|
"`finalize_upload`, `mint_render_token`; and the host tools are plain verbs too (`bash`,",
|
|
3858
3894
|
"`read`, `edit`). Mind the collision: a bare `read` or `edit` is the HOST tool, acting on the",
|
|
3859
3895
|
"local machine \u2014 a workspace read is `query` with the operation name `files.read`. The",
|
|
@@ -5038,8 +5074,8 @@ var init_addendum3 = __esm({
|
|
|
5038
5074
|
// in sat two lines under the instruction to invoke the exact qualified names,
|
|
5039
5075
|
// so the sentence contradicted itself. The bare `query` / `mutate` below stay:
|
|
5040
5076
|
// that sentence is about operation names, after the qualified forms are given.
|
|
5041
|
-
"The ancillary tools are `
|
|
5042
|
-
"`
|
|
5077
|
+
"The ancillary tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`,",
|
|
5078
|
+
"`mcp__cabane__begin_upload`, `mcp__cabane__finalize_upload` and",
|
|
5043
5079
|
"`mcp__cabane__mint_render_token` \u2014 all possibly deferred too.",
|
|
5044
5080
|
"There is no Cabane `read`/`write`/`search` tool \u2014 those are",
|
|
5045
5081
|
"operation NAMES you pass to `query` / `mutate`. One more harness fact: you can interleave",
|
package/dist/runtime.js
CHANGED
|
@@ -3256,7 +3256,8 @@ var turnEventSchema = z6.discriminatedUnion("type", [
|
|
|
3256
3256
|
// `progress` row); `terminal: true` is the turn's closing reply (commits as
|
|
3257
3257
|
// the `final` row).
|
|
3258
3258
|
z6.object({ type: z6.literal("text"), body: z6.string(), terminal: z6.boolean() }),
|
|
3259
|
-
// Runtime/provider-authored prose
|
|
3259
|
+
// Runtime/provider-authored prose about the turn — a failure's explanation, or
|
|
3260
|
+
// a line the harness wrote on a turn it recovered (CT1750). Unlike
|
|
3260
3261
|
// `text`, this is not the agent's narration or reply: the pump persists it as
|
|
3261
3262
|
// `runtime_notice`, and the transcript renders it in the shared SystemNote
|
|
3262
3263
|
// voice. Adapters emit it only from positive runtime evidence; the web never
|
|
@@ -3450,10 +3451,12 @@ function isContentBearingEvent(event) {
|
|
|
3450
3451
|
return normalizeCommitText(event.body) !== "";
|
|
3451
3452
|
case "thinking":
|
|
3452
3453
|
return normalizeCommitText(event.text) !== "";
|
|
3453
|
-
case "runtime_notice":
|
|
3454
|
-
return normalizeCommitText(event.body) !== "";
|
|
3455
3454
|
case "tool":
|
|
3456
3455
|
return true;
|
|
3456
|
+
// CT1750: a runtime notice is shown, but it is the harness speaking about
|
|
3457
|
+
// the turn, not the turn's output — a success whose only output is a CLI
|
|
3458
|
+
// line still produced nothing.
|
|
3459
|
+
case "runtime_notice":
|
|
3457
3460
|
case "session":
|
|
3458
3461
|
case "result":
|
|
3459
3462
|
return false;
|
|
@@ -3987,8 +3990,7 @@ function turnControlPromptGuidance(prefix) {
|
|
|
3987
3990
|
var CLAUDE_CODE_ADDENDUM = [
|
|
3988
3991
|
"On this harness the Cabane tools register under MCP prefixes. The workspace tools are",
|
|
3989
3992
|
"`mcp__cabane__query`, `mcp__cabane__mutate` and `mcp__cabane__describe`; the ancillary",
|
|
3990
|
-
"tools are `
|
|
3991
|
-
"`mcp__cabane__read_binary`, `mcp__cabane__upload`, `mcp__cabane__begin_upload`,",
|
|
3993
|
+
"tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`, `mcp__cabane__begin_upload`,",
|
|
3992
3994
|
"`mcp__cabane__finalize_upload`, and `mcp__cabane__mint_render_token`.",
|
|
3993
3995
|
turnControlPromptGuidance("mcp__cabane_companion__"),
|
|
3994
3996
|
"Where this prompt names a tool by its bare verb, call",
|
|
@@ -4295,6 +4297,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4295
4297
|
let lastRequestContextTokens;
|
|
4296
4298
|
let resolvedModel;
|
|
4297
4299
|
const signals = { sawRejectedLimit: false };
|
|
4300
|
+
let preInitNotice = null;
|
|
4298
4301
|
try {
|
|
4299
4302
|
for await (const msg of iter) {
|
|
4300
4303
|
if (ctx.signal.aborted) return;
|
|
@@ -4320,12 +4323,19 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4320
4323
|
if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
|
|
4321
4324
|
const failure = classifyPreInitFrame(msg, signals);
|
|
4322
4325
|
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
4326
|
+
if (msg.type === "assistant" && isHarnessAuthored(msg))
|
|
4327
|
+
preInitNotice = harnessNoticeText(msg);
|
|
4323
4328
|
break;
|
|
4324
4329
|
} else if (msg.type === "assistant") {
|
|
4325
4330
|
const assistantErr = msg.error;
|
|
4326
4331
|
if (typeof assistantErr === "string" && assistantErr.length > 0) {
|
|
4327
4332
|
signals.lastAssistantError = assistantErr;
|
|
4328
4333
|
}
|
|
4334
|
+
if (isHarnessAuthored(msg)) {
|
|
4335
|
+
await emitHarnessMessage(msg, emit, pending, buffer);
|
|
4336
|
+
yield* drain(out);
|
|
4337
|
+
continue;
|
|
4338
|
+
}
|
|
4329
4339
|
const reqContext = readRequestContextTokens(msg);
|
|
4330
4340
|
if (reqContext !== void 0) lastRequestContextTokens = reqContext;
|
|
4331
4341
|
await processAssistantMessage(msg, emit, pending, buffer);
|
|
@@ -4387,6 +4397,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4387
4397
|
const failure = classifyFailure(signals, { text: "" });
|
|
4388
4398
|
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
4389
4399
|
}
|
|
4400
|
+
if (preInitNotice) yield { type: "runtime_notice", body: preInitNotice };
|
|
4390
4401
|
}
|
|
4391
4402
|
const heldFailure = !ok ? classifyRuntimeNoticeText(buffer.held) : null;
|
|
4392
4403
|
const terminalFailure = !ok ? decodeFailureReason(resultReason) : null;
|
|
@@ -4453,6 +4464,30 @@ function preInitToolActivity(msg) {
|
|
|
4453
4464
|
const kind = msg.type === "assistant" ? "tool_use" : msg.type === "user" ? "tool_result" : null;
|
|
4454
4465
|
return kind !== null && contentBlocks(msg).some((b) => b.type === kind);
|
|
4455
4466
|
}
|
|
4467
|
+
function isHarnessAuthored(msg) {
|
|
4468
|
+
const error = msg.error;
|
|
4469
|
+
if (typeof error === "string" && error.length > 0) return true;
|
|
4470
|
+
return msg.message?.model === "<synthetic>";
|
|
4471
|
+
}
|
|
4472
|
+
var HARNESS_PLACEHOLDERS = /* @__PURE__ */ new Set(["No response requested.", "(no content)"]);
|
|
4473
|
+
function harnessNoticeText(msg) {
|
|
4474
|
+
const body = contentBlocks(msg).flatMap((b) => b.type === "text" && typeof b.text === "string" ? [b.text] : []).join("\n\n").trim();
|
|
4475
|
+
return body && !HARNESS_PLACEHOLDERS.has(body) ? body : null;
|
|
4476
|
+
}
|
|
4477
|
+
async function emitHarnessMessage(msg, emit, pending, buffer) {
|
|
4478
|
+
const body = harnessNoticeText(msg);
|
|
4479
|
+
if (body) emit({ type: "runtime_notice", body });
|
|
4480
|
+
const toolUses = contentBlocks(msg).filter((b) => b.type === "tool_use");
|
|
4481
|
+
if (toolUses.length > 0) {
|
|
4482
|
+
const inner = msg.message ?? {};
|
|
4483
|
+
await processAssistantMessage(
|
|
4484
|
+
{ message: { ...inner, content: toolUses } },
|
|
4485
|
+
emit,
|
|
4486
|
+
pending,
|
|
4487
|
+
buffer
|
|
4488
|
+
);
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4456
4491
|
function contentBlocks(msg) {
|
|
4457
4492
|
const content = msg.message?.content;
|
|
4458
4493
|
return Array.isArray(content) ? content : [];
|
|
@@ -4556,7 +4591,7 @@ async function* runWithResumeRecovery(queryFn, req, signal, initial, augmentOpti
|
|
|
4556
4591
|
yield* sdkEvents(queryFn, fresh.req, signal, options, fresh.degraded);
|
|
4557
4592
|
return;
|
|
4558
4593
|
}
|
|
4559
|
-
emitted = true;
|
|
4594
|
+
if (event.type !== "runtime_notice") emitted = true;
|
|
4560
4595
|
yield event;
|
|
4561
4596
|
}
|
|
4562
4597
|
}
|
|
@@ -5192,7 +5227,7 @@ var OPENCODE_ADDENDUM = [
|
|
|
5192
5227
|
"Every tool here goes by its plain name: the workspace tools are `query`, `mutate` and",
|
|
5193
5228
|
"`describe`.",
|
|
5194
5229
|
turnControlPromptGuidance(""),
|
|
5195
|
-
"The ancillary tools are `
|
|
5230
|
+
"The ancillary tools are `read_binary`, `upload`, `begin_upload`,",
|
|
5196
5231
|
"`finalize_upload`, `mint_render_token`; and the host tools are plain verbs too (`bash`,",
|
|
5197
5232
|
"`read`, `edit`). Mind the collision: a bare `read` or `edit` is the HOST tool, acting on the",
|
|
5198
5233
|
"local machine \u2014 a workspace read is `query` with the operation name `files.read`. The",
|
|
@@ -6297,8 +6332,8 @@ var CODEX_ADDENDUM = [
|
|
|
6297
6332
|
// in sat two lines under the instruction to invoke the exact qualified names,
|
|
6298
6333
|
// so the sentence contradicted itself. The bare `query` / `mutate` below stay:
|
|
6299
6334
|
// that sentence is about operation names, after the qualified forms are given.
|
|
6300
|
-
"The ancillary tools are `
|
|
6301
|
-
"`
|
|
6335
|
+
"The ancillary tools are `mcp__cabane__read_binary`, `mcp__cabane__upload`,",
|
|
6336
|
+
"`mcp__cabane__begin_upload`, `mcp__cabane__finalize_upload` and",
|
|
6302
6337
|
"`mcp__cabane__mint_render_token` \u2014 all possibly deferred too.",
|
|
6303
6338
|
"There is no Cabane `read`/`write`/`search` tool \u2014 those are",
|
|
6304
6339
|
"operation NAMES you pass to `query` / `mutate`. One more harness fact: you can interleave",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.142",
|
|
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",
|