@cabane/companion 0.6.66 → 0.6.67
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 +21 -135
- package/dist/runtime.js +21 -135
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2287,7 +2287,6 @@ var ACTIVE_RUN_OUTBOX_SEQ = 0;
|
|
|
2287
2287
|
function activeRunOutboxKey(conversationId, agentId) {
|
|
2288
2288
|
return `activerun:${conversationId}:${agentId}`;
|
|
2289
2289
|
}
|
|
2290
|
-
var ACTIVE_CONVERSATION_HEADER = "x-cabane-active-conversation";
|
|
2291
2290
|
var CabaneApi = class {
|
|
2292
2291
|
constructor(opts) {
|
|
2293
2292
|
this.opts = opts;
|
|
@@ -2425,45 +2424,6 @@ var CabaneApi = class {
|
|
|
2425
2424
|
getConversation(workspaceId, conversationId) {
|
|
2426
2425
|
return this.request("GET", `/api/workspaces/${workspaceId}/conversations/${conversationId}`);
|
|
2427
2426
|
}
|
|
2428
|
-
// CT339: spawn a sub-agent conversation — the public create-conversation
|
|
2429
|
-
// endpoint (`POST /conversations/messages`) with an addressed agent ask,
|
|
2430
|
-
// the machinery the `sub_agent` turn-control tool is sugar over. Two things make
|
|
2431
|
-
// it distinct from an ordinary `request` call, so it does its own `fetch`:
|
|
2432
|
-
// - a PER-CALL bearer — the turn's OBO token when the API minted one, else the
|
|
2433
|
-
// companion PAT — so the spawn carries the same authority as the agent's other
|
|
2434
|
-
// cabane calls this turn (mirrors build-options' `turnToken ?? agentPat`);
|
|
2435
|
-
// - the `x-cabane-active-conversation` header naming the caller's turn, which
|
|
2436
|
-
// the server verifies against the live run to resolve the caller pair for
|
|
2437
|
-
// the explicit child's birth subscription (the same origin that stamps
|
|
2438
|
-
// provenance).
|
|
2439
|
-
// Single-shot (no outbox/retry): a `sub_agent` spawn is a live, user-visible
|
|
2440
|
-
// action, and its result is returned to the agent immediately. Returns
|
|
2441
|
-
// `{ status, body }` un-thrown so the caller maps validation errors
|
|
2442
|
-
// to a legible tool result rather than an exception.
|
|
2443
|
-
async createSubAgentConversation(workspaceId, activeConversationId, bearer, body) {
|
|
2444
|
-
const res = await fetch(`${this.base}/api/workspaces/${workspaceId}/conversations/messages`, {
|
|
2445
|
-
method: "POST",
|
|
2446
|
-
headers: {
|
|
2447
|
-
Authorization: `Bearer ${bearer}`,
|
|
2448
|
-
"content-type": "application/json",
|
|
2449
|
-
accept: "application/json",
|
|
2450
|
-
// CT238/CT339: the per-turn active-conversation header — server-verified
|
|
2451
|
-
// against the caller's live run before it's honored (never client-trusted).
|
|
2452
|
-
[ACTIVE_CONVERSATION_HEADER]: activeConversationId
|
|
2453
|
-
},
|
|
2454
|
-
body: JSON.stringify(body)
|
|
2455
|
-
});
|
|
2456
|
-
const raw = await res.text();
|
|
2457
|
-
let parsed = void 0;
|
|
2458
|
-
if (raw.length > 0) {
|
|
2459
|
-
try {
|
|
2460
|
-
parsed = JSON.parse(raw);
|
|
2461
|
-
} catch {
|
|
2462
|
-
parsed = raw;
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
return { status: res.status, body: parsed };
|
|
2466
|
-
}
|
|
2467
2427
|
// SJ383: companion-only participant ops. All three authenticate with the
|
|
2468
2428
|
// workspace's agent-bound PAT (passed as `token` on this client) — never
|
|
2469
2429
|
// the user PAT.
|
|
@@ -3407,7 +3367,7 @@ var turnRequestSchema = z8.object({
|
|
|
3407
3367
|
// EXTERNAL adapters (Codex / opencode) mount it by URL under the key
|
|
3408
3368
|
// `cabane_companion` — using the same `bearer` (the turn token) and the same
|
|
3409
3369
|
// active-conversation header they send to the `cabane` server — so their
|
|
3410
|
-
// agents get `ask`/`wake_me`/`send`/`
|
|
3370
|
+
// agents get `ask`/`reply_to`/`wake_me`/`send`/`skip_turn`, the
|
|
3411
3371
|
// verbs they can't get from the companion's in-process SDK server. Optional:
|
|
3412
3372
|
// claude-code ignores it (it mounts the in-process instance instead), and
|
|
3413
3373
|
// every existing `cabane`-block fixture keeps parsing unchanged; the
|
|
@@ -3770,7 +3730,7 @@ var CLAUDE_CODE_ADDENDUM = [
|
|
|
3770
3730
|
"`mcp__cabane__finalize_upload`, and `mcp__cabane__mint_render_token`; the turn tools are",
|
|
3771
3731
|
"`mcp__cabane_companion__ask`, `mcp__cabane_companion__reply_to`,",
|
|
3772
3732
|
"`mcp__cabane_companion__wake_me`,",
|
|
3773
|
-
"`mcp__cabane_companion__send`,
|
|
3733
|
+
"`mcp__cabane_companion__send`, and",
|
|
3774
3734
|
"`mcp__cabane_companion__skip_turn`. Where this prompt names a tool by its bare verb, call",
|
|
3775
3735
|
"the prefixed form from your tool list. Your harness's own tools (shell, local file",
|
|
3776
3736
|
"read/edit) keep their ordinary names and act on the local machine only."
|
|
@@ -3882,7 +3842,7 @@ function decideResume(stored, currentCwd) {
|
|
|
3882
3842
|
|
|
3883
3843
|
// packages/agent-runtime/src/claude-code/options.ts
|
|
3884
3844
|
var CABANE_MCP_SERVER = "cabane";
|
|
3885
|
-
var
|
|
3845
|
+
var ACTIVE_CONVERSATION_HEADER = "x-cabane-active-conversation";
|
|
3886
3846
|
async function preToolUseHook(input) {
|
|
3887
3847
|
const toolInput = "tool_input" in input && input.tool_input && typeof input.tool_input === "object" ? input.tool_input : {};
|
|
3888
3848
|
const toolName = "tool_name" in input ? input.tool_name : void 0;
|
|
@@ -3919,7 +3879,7 @@ function buildClaudeCodeOptions(req, augment) {
|
|
|
3919
3879
|
url: req.cabane.mcpUrl,
|
|
3920
3880
|
headers: {
|
|
3921
3881
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
3922
|
-
[
|
|
3882
|
+
[ACTIVE_CONVERSATION_HEADER]: req.cabane.activeConversationId
|
|
3923
3883
|
},
|
|
3924
3884
|
// Claude Code 2.1.x defers every MCP tool behind tool-search by default;
|
|
3925
3885
|
// for cabane — the always-needed core toolset — force it into the turn-1
|
|
@@ -4741,7 +4701,7 @@ function selectAdapter(registry, runtime) {
|
|
|
4741
4701
|
// packages/agent-runtime/src/opencode/addendum.ts
|
|
4742
4702
|
var OPENCODE_ADDENDUM = [
|
|
4743
4703
|
"Every tool here goes by its plain name: the workspace tool is `sdk`; the turn tools are",
|
|
4744
|
-
"`ask`, `reply_to`, `wake_me`, `send`, `
|
|
4704
|
+
"`ask`, `reply_to`, `wake_me`, `send`, `skip_turn`; the ancillary tools are",
|
|
4745
4705
|
"`list_workspaces`, `read_binary`, `upload`, `begin_upload`, `finalize_upload`,",
|
|
4746
4706
|
"`mint_render_token`; and the host tools are plain verbs too (`bash`, `read`, `edit`). Mind",
|
|
4747
4707
|
"the collision: a bare `read` or `edit` is the HOST tool, acting on the local machine \u2014 a",
|
|
@@ -5008,7 +4968,7 @@ function parseOpencodeModel(model) {
|
|
|
5008
4968
|
// packages/agent-runtime/src/opencode/run-spec.ts
|
|
5009
4969
|
var CABANE_MCP_SERVER2 = "cabane";
|
|
5010
4970
|
var TURN_CONTROL_MCP_SERVER = "cabane_companion";
|
|
5011
|
-
var
|
|
4971
|
+
var ACTIVE_CONVERSATION_HEADER2 = "x-cabane-active-conversation";
|
|
5012
4972
|
function buildRunSpec(req, resumeSessionId) {
|
|
5013
4973
|
const { policy, config } = req;
|
|
5014
4974
|
const directory = req.local.cwd ?? "";
|
|
@@ -5065,7 +5025,7 @@ function buildMcp(req) {
|
|
|
5065
5025
|
url: req.cabane.mcpUrl,
|
|
5066
5026
|
headers: {
|
|
5067
5027
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
5068
|
-
[
|
|
5028
|
+
[ACTIVE_CONVERSATION_HEADER2]: req.cabane.activeConversationId
|
|
5069
5029
|
},
|
|
5070
5030
|
enabled: true
|
|
5071
5031
|
};
|
|
@@ -5075,7 +5035,7 @@ function buildMcp(req) {
|
|
|
5075
5035
|
url: req.cabane.turnControlUrl,
|
|
5076
5036
|
headers: {
|
|
5077
5037
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
5078
|
-
[
|
|
5038
|
+
[ACTIVE_CONVERSATION_HEADER2]: req.cabane.activeConversationId
|
|
5079
5039
|
},
|
|
5080
5040
|
enabled: true
|
|
5081
5041
|
};
|
|
@@ -5718,7 +5678,7 @@ var CODEX_ADDENDUM = [
|
|
|
5718
5678
|
"the deferred-tool inventory and invoke that exact qualified name; never conclude the SDK is",
|
|
5719
5679
|
"absent without attempting discovery and invocation, and if a call fails, report the recorded",
|
|
5720
5680
|
"error. The turn tools are `mcp__cabane_companion__ask` / `reply_to` / `wake_me` /",
|
|
5721
|
-
"`send` / `
|
|
5681
|
+
"`send` / `skip_turn`, and the ancillary tools",
|
|
5722
5682
|
"`mcp__cabane__list_workspaces` /",
|
|
5723
5683
|
"`read_binary` / `upload` / `begin_upload` / `finalize_upload` / `mint_render_token` \u2014 all",
|
|
5724
5684
|
"possibly deferred too. There is no Cabane `read`/`write`/`search` tool \u2014 those are",
|
|
@@ -6082,7 +6042,7 @@ function parseCodexModel(model) {
|
|
|
6082
6042
|
// packages/agent-runtime/src/codex/run-spec.ts
|
|
6083
6043
|
var CABANE_MCP_SERVER3 = "cabane";
|
|
6084
6044
|
var TURN_CONTROL_MCP_SERVER2 = "cabane_companion";
|
|
6085
|
-
var
|
|
6045
|
+
var ACTIVE_CONVERSATION_HEADER3 = "x-cabane-active-conversation";
|
|
6086
6046
|
var ENV_ENVELOPE_KEYS = [
|
|
6087
6047
|
"CABANE_PLAYGROUND",
|
|
6088
6048
|
"CABANE_PLAYGROUND_BIN",
|
|
@@ -6166,7 +6126,7 @@ function buildConfig(req, baseInstructionsFile) {
|
|
|
6166
6126
|
url: req.cabane.mcpUrl,
|
|
6167
6127
|
http_headers: {
|
|
6168
6128
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
6169
|
-
[
|
|
6129
|
+
[ACTIVE_CONVERSATION_HEADER3]: req.cabane.activeConversationId
|
|
6170
6130
|
},
|
|
6171
6131
|
default_tools_approval_mode: "approve"
|
|
6172
6132
|
};
|
|
@@ -6175,7 +6135,7 @@ function buildConfig(req, baseInstructionsFile) {
|
|
|
6175
6135
|
url: req.cabane.turnControlUrl,
|
|
6176
6136
|
http_headers: {
|
|
6177
6137
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
6178
|
-
[
|
|
6138
|
+
[ACTIVE_CONVERSATION_HEADER3]: req.cabane.activeConversationId
|
|
6179
6139
|
},
|
|
6180
6140
|
default_tools_approval_mode: "approve"
|
|
6181
6141
|
};
|
|
@@ -7229,8 +7189,6 @@ var ASK_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${ASK_TOOL}`;
|
|
|
7229
7189
|
var REPLY_TO_TOOL = "reply_to";
|
|
7230
7190
|
var REPLY_TO_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${REPLY_TO_TOOL}`;
|
|
7231
7191
|
var MAX_ASK_ITEMS = 20;
|
|
7232
|
-
var SUB_AGENT_TOOL = "sub_agent";
|
|
7233
|
-
var SUB_AGENT_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${SUB_AGENT_TOOL}`;
|
|
7234
7192
|
var WAKE_ME_TOOL = "wake_me";
|
|
7235
7193
|
var WAKE_ME_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${WAKE_ME_TOOL}`;
|
|
7236
7194
|
var CANCEL_WAKE_TOOL = "cancel_wake";
|
|
@@ -7269,8 +7227,8 @@ function resolveDeclaredReplyField(input) {
|
|
|
7269
7227
|
);
|
|
7270
7228
|
const askRaised = Boolean(input.askState.targetUserId);
|
|
7271
7229
|
const wakeArmed = input.wakeState.afterSeconds !== null || input.wakeState.at !== null;
|
|
7272
|
-
if (outwardSend || askRaised || wakeArmed
|
|
7273
|
-
return { answersMessageId: input.owedReplyMessageId };
|
|
7230
|
+
if (outwardSend || askRaised || wakeArmed) return {};
|
|
7231
|
+
return { answersMessageId: input.owedReplyMessageId, answersAutoDeclared: true };
|
|
7274
7232
|
}
|
|
7275
7233
|
function wakeCommitField(state) {
|
|
7276
7234
|
if (state.cancelled) return { wake: { cancel: true } };
|
|
@@ -7284,7 +7242,7 @@ function wakeCommitField(state) {
|
|
|
7284
7242
|
}
|
|
7285
7243
|
};
|
|
7286
7244
|
}
|
|
7287
|
-
function createTurnControlMcpServer(sendState, skipState, askState,
|
|
7245
|
+
function createTurnControlMcpServer(sendState, skipState, askState, wakeState, replyState, controlOrder) {
|
|
7288
7246
|
return createSdkMcpServer({
|
|
7289
7247
|
name: COMPANION_LOCAL_MCP_SERVER,
|
|
7290
7248
|
version: "0.0.0",
|
|
@@ -7420,42 +7378,6 @@ function createTurnControlMcpServer(sendState, skipState, askState, subAgentCrea
|
|
|
7420
7378
|
{ annotations: { readOnlyHint: true, openWorldHint: false }, alwaysLoad: true }
|
|
7421
7379
|
)
|
|
7422
7380
|
] : [],
|
|
7423
|
-
...subAgentCreate ? [
|
|
7424
|
-
tool(
|
|
7425
|
-
SUB_AGENT_TOOL,
|
|
7426
|
-
"Ask into a child conversation \u2014 a bounded question or piece of work whose ANSWER returns to you automatically. This creates the child and dispatches its agent now, returning the child's `conversationId` immediately; the answer lands LATER as a message in THIS conversation, and you're woken once every child you asked this turn has answered. So DON'T wait: after asking, finish whatever else this turn can do and end your turn (never poll the child with reads in a loop \u2014 the wake is automatic and it carries the answer). Parallel fan-out = call this N times in one turn (they run concurrently; ONE wake carrying all the answers); series = one call per turn. `prompt` is the ask itself \u2014 write it as a request that expects an answer back, not a role you are assigning or a job description: self-contained (the child starts fresh, with only this prompt), and say what shape the answer should take. `agentId` (optional) asks a PEER instead of yourself \u2014 same mechanics, a different mind (use for capability/context you lack); default (self) is a fresh context window of you. `title` (optional) names the child thread (answers link it, so a legible title helps). A single child buys no wall-clock time (you idle either way) \u2014 it pays when the callee has capability/context you lack, or to isolate a big read from your own session; the real win is fan-out. Don't ask a child for a lookup you can do in-turn with your own tools. The answer returns to YOU to act on \u2014 reach for it when you're the consumer of the output, not as a way to notify a human: if a person just wants to read the result, dispatch a plain (no-return) conversation and link it instead.",
|
|
7427
|
-
{
|
|
7428
|
-
prompt: z13.string().min(1).max(65536).describe(
|
|
7429
|
-
"The ask \u2014 a self-contained request that expects an answer back (the child starts fresh: only this prompt + the thread it lands in). Say what shape the answer should take."
|
|
7430
|
-
),
|
|
7431
|
-
agentId: z13.string().uuid().optional().describe(
|
|
7432
|
-
"Optional peer to run the sub-agent as (a workspace agent id, from your turn context's roster); omit to spawn yourself with a fresh context window."
|
|
7433
|
-
),
|
|
7434
|
-
title: z13.string().max(200).optional().describe("Optional title for the child thread (result chips link it).")
|
|
7435
|
-
},
|
|
7436
|
-
async (args) => {
|
|
7437
|
-
const result = await subAgentCreate(args);
|
|
7438
|
-
if (!result.ok) {
|
|
7439
|
-
return {
|
|
7440
|
-
content: [{ type: "text", text: result.error }],
|
|
7441
|
-
isError: true
|
|
7442
|
-
};
|
|
7443
|
-
}
|
|
7444
|
-
return {
|
|
7445
|
-
content: [
|
|
7446
|
-
{
|
|
7447
|
-
type: "text",
|
|
7448
|
-
text: JSON.stringify({
|
|
7449
|
-
conversationId: result.conversationId,
|
|
7450
|
-
note: "Asked. Don't wait for it \u2014 finish what else this turn can do, then end your turn; the answer posts back here and you're woken once every child you asked this turn has answered."
|
|
7451
|
-
})
|
|
7452
|
-
}
|
|
7453
|
-
]
|
|
7454
|
-
};
|
|
7455
|
-
},
|
|
7456
|
-
{ annotations: { readOnlyHint: false, openWorldHint: true }, alwaysLoad: true }
|
|
7457
|
-
)
|
|
7458
|
-
] : [],
|
|
7459
7381
|
...wakeState ? [
|
|
7460
7382
|
tool(
|
|
7461
7383
|
WAKE_ME_TOOL,
|
|
@@ -7995,7 +7917,10 @@ var TurnCommitter = class {
|
|
|
7995
7917
|
}
|
|
7996
7918
|
// The declared reply: explicit `reply_to` first, else the runtime's own
|
|
7997
7919
|
// declaration for the turn that just answers — see
|
|
7998
|
-
// `resolveDeclaredReplyField` for the whole rule and its reasons.
|
|
7920
|
+
// `resolveDeclaredReplyField` for the whole rule and its reasons. CT1224: an
|
|
7921
|
+
// auto-declaration is flagged as such on the wire, because the server drops it
|
|
7922
|
+
// when the turn also opened a conversation (a fan-out is mid-arc). An explicit
|
|
7923
|
+
// `reply_to` carries no flag and always stands.
|
|
7999
7924
|
answersField(kind) {
|
|
8000
7925
|
return resolveDeclaredReplyField({
|
|
8001
7926
|
kind,
|
|
@@ -8004,8 +7929,7 @@ var TurnCommitter = class {
|
|
|
8004
7929
|
replyState: this.deps.replyState,
|
|
8005
7930
|
sendState: this.deps.sendState,
|
|
8006
7931
|
askState: this.deps.askState,
|
|
8007
|
-
wakeState: this.deps.wakeState
|
|
8008
|
-
spawnedSubAgent: this.deps.spawnedSubAgent()
|
|
7932
|
+
wakeState: this.deps.wakeState
|
|
8009
7933
|
});
|
|
8010
7934
|
}
|
|
8011
7935
|
// Carry both halves of the addressed send. The server writes `dispatchBody`
|
|
@@ -8126,17 +8050,6 @@ function describeErrorBody(body) {
|
|
|
8126
8050
|
if (text.length === 0) return void 0;
|
|
8127
8051
|
return text.length > ERROR_BODY_LOG_CAP ? `${text.slice(0, ERROR_BODY_LOG_CAP)}\u2026` : text;
|
|
8128
8052
|
}
|
|
8129
|
-
function describeSubAgentError(status2, body) {
|
|
8130
|
-
const code = body && typeof body === "object" && "error" in body ? String(body.error) : void 0;
|
|
8131
|
-
switch (code) {
|
|
8132
|
-
case "dispatch_agent_not_found":
|
|
8133
|
-
return "sub_agent: no live agent in this workspace matches that `agentId`. Check the roster in your turn context, or omit `agentId` to spawn yourself.";
|
|
8134
|
-
case "dispatch_ask_requires_dispatch":
|
|
8135
|
-
return `sub_agent: the spawn was rejected (${code}). This is a turn-context problem, not something to retry blindly \u2014 report it rather than looping.`;
|
|
8136
|
-
default:
|
|
8137
|
-
return `sub_agent: the spawn failed (${code ?? `HTTP ${status2}`}).`;
|
|
8138
|
-
}
|
|
8139
|
-
}
|
|
8140
8053
|
var Dispatcher = class {
|
|
8141
8054
|
constructor(opts) {
|
|
8142
8055
|
this.opts = opts;
|
|
@@ -8499,36 +8412,10 @@ ${reason}`,
|
|
|
8499
8412
|
const wakeState = createWakeState();
|
|
8500
8413
|
const replyState = createReplyState();
|
|
8501
8414
|
const controlOrder = createTurnControlOrder();
|
|
8502
|
-
let spawnedSubAgent = false;
|
|
8503
|
-
const subAgentCreate = async (args) => {
|
|
8504
|
-
const dispatchTarget = args.agentId ?? payload.agentId;
|
|
8505
|
-
const projectId = turnContext.conversation.projectId;
|
|
8506
|
-
const { status: status2, body } = await this.opts.api.createSubAgentConversation(
|
|
8507
|
-
workspaceId,
|
|
8508
|
-
payload.conversationId,
|
|
8509
|
-
turnContext.turnToken ?? this.opts.credential,
|
|
8510
|
-
{
|
|
8511
|
-
...projectId ? { projectId } : {},
|
|
8512
|
-
...args.title ? { title: args.title } : {},
|
|
8513
|
-
body: args.prompt,
|
|
8514
|
-
dispatch: dispatchTarget,
|
|
8515
|
-
dispatchAsk: true,
|
|
8516
|
-
parentConversationId: payload.conversationId
|
|
8517
|
-
}
|
|
8518
|
-
);
|
|
8519
|
-
if (status2 >= 400) return { ok: false, error: describeSubAgentError(status2, body) };
|
|
8520
|
-
const conversationId = body?.conversation?.id;
|
|
8521
|
-
if (!conversationId) {
|
|
8522
|
-
return { ok: false, error: "sub_agent: the spawn returned no conversation id." };
|
|
8523
|
-
}
|
|
8524
|
-
spawnedSubAgent = true;
|
|
8525
|
-
return { ok: true, conversationId };
|
|
8526
|
-
};
|
|
8527
8415
|
const turnControlServer = createTurnControlMcpServer(
|
|
8528
8416
|
sendState,
|
|
8529
8417
|
skipState,
|
|
8530
8418
|
askState,
|
|
8531
|
-
subAgentCreate,
|
|
8532
8419
|
wakeState,
|
|
8533
8420
|
replyState,
|
|
8534
8421
|
controlOrder
|
|
@@ -8664,8 +8551,7 @@ ${reason}`,
|
|
|
8664
8551
|
replyState,
|
|
8665
8552
|
// The ledger-derived owed reply, for the runtime's own declaration when
|
|
8666
8553
|
// the agent doesn't call `reply_to` (resolveDeclaredReplyField).
|
|
8667
|
-
owedReplyMessageId: turnContext.owedReplyMessageId ?? null
|
|
8668
|
-
spawnedSubAgent: () => spawnedSubAgent
|
|
8554
|
+
owedReplyMessageId: turnContext.owedReplyMessageId ?? null
|
|
8669
8555
|
});
|
|
8670
8556
|
const usesHttpTurnControl = turnRuntime === "codex" || turnRuntime === "opencode";
|
|
8671
8557
|
let turnControlIntentFetched = false;
|
package/dist/runtime.js
CHANGED
|
@@ -1707,7 +1707,6 @@ var ACTIVE_RUN_OUTBOX_SEQ = 0;
|
|
|
1707
1707
|
function activeRunOutboxKey(conversationId, agentId) {
|
|
1708
1708
|
return `activerun:${conversationId}:${agentId}`;
|
|
1709
1709
|
}
|
|
1710
|
-
var ACTIVE_CONVERSATION_HEADER = "x-cabane-active-conversation";
|
|
1711
1710
|
var CabaneApi = class {
|
|
1712
1711
|
constructor(opts) {
|
|
1713
1712
|
this.opts = opts;
|
|
@@ -1845,45 +1844,6 @@ var CabaneApi = class {
|
|
|
1845
1844
|
getConversation(workspaceId, conversationId) {
|
|
1846
1845
|
return this.request("GET", `/api/workspaces/${workspaceId}/conversations/${conversationId}`);
|
|
1847
1846
|
}
|
|
1848
|
-
// CT339: spawn a sub-agent conversation — the public create-conversation
|
|
1849
|
-
// endpoint (`POST /conversations/messages`) with an addressed agent ask,
|
|
1850
|
-
// the machinery the `sub_agent` turn-control tool is sugar over. Two things make
|
|
1851
|
-
// it distinct from an ordinary `request` call, so it does its own `fetch`:
|
|
1852
|
-
// - a PER-CALL bearer — the turn's OBO token when the API minted one, else the
|
|
1853
|
-
// companion PAT — so the spawn carries the same authority as the agent's other
|
|
1854
|
-
// cabane calls this turn (mirrors build-options' `turnToken ?? agentPat`);
|
|
1855
|
-
// - the `x-cabane-active-conversation` header naming the caller's turn, which
|
|
1856
|
-
// the server verifies against the live run to resolve the caller pair for
|
|
1857
|
-
// the explicit child's birth subscription (the same origin that stamps
|
|
1858
|
-
// provenance).
|
|
1859
|
-
// Single-shot (no outbox/retry): a `sub_agent` spawn is a live, user-visible
|
|
1860
|
-
// action, and its result is returned to the agent immediately. Returns
|
|
1861
|
-
// `{ status, body }` un-thrown so the caller maps validation errors
|
|
1862
|
-
// to a legible tool result rather than an exception.
|
|
1863
|
-
async createSubAgentConversation(workspaceId, activeConversationId, bearer, body) {
|
|
1864
|
-
const res = await fetch(`${this.base}/api/workspaces/${workspaceId}/conversations/messages`, {
|
|
1865
|
-
method: "POST",
|
|
1866
|
-
headers: {
|
|
1867
|
-
Authorization: `Bearer ${bearer}`,
|
|
1868
|
-
"content-type": "application/json",
|
|
1869
|
-
accept: "application/json",
|
|
1870
|
-
// CT238/CT339: the per-turn active-conversation header — server-verified
|
|
1871
|
-
// against the caller's live run before it's honored (never client-trusted).
|
|
1872
|
-
[ACTIVE_CONVERSATION_HEADER]: activeConversationId
|
|
1873
|
-
},
|
|
1874
|
-
body: JSON.stringify(body)
|
|
1875
|
-
});
|
|
1876
|
-
const raw = await res.text();
|
|
1877
|
-
let parsed = void 0;
|
|
1878
|
-
if (raw.length > 0) {
|
|
1879
|
-
try {
|
|
1880
|
-
parsed = JSON.parse(raw);
|
|
1881
|
-
} catch {
|
|
1882
|
-
parsed = raw;
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
return { status: res.status, body: parsed };
|
|
1886
|
-
}
|
|
1887
1847
|
// SJ383: companion-only participant ops. All three authenticate with the
|
|
1888
1848
|
// workspace's agent-bound PAT (passed as `token` on this client) — never
|
|
1889
1849
|
// the user PAT.
|
|
@@ -2906,7 +2866,7 @@ var turnRequestSchema = z8.object({
|
|
|
2906
2866
|
// EXTERNAL adapters (Codex / opencode) mount it by URL under the key
|
|
2907
2867
|
// `cabane_companion` — using the same `bearer` (the turn token) and the same
|
|
2908
2868
|
// active-conversation header they send to the `cabane` server — so their
|
|
2909
|
-
// agents get `ask`/`wake_me`/`send`/`
|
|
2869
|
+
// agents get `ask`/`reply_to`/`wake_me`/`send`/`skip_turn`, the
|
|
2910
2870
|
// verbs they can't get from the companion's in-process SDK server. Optional:
|
|
2911
2871
|
// claude-code ignores it (it mounts the in-process instance instead), and
|
|
2912
2872
|
// every existing `cabane`-block fixture keeps parsing unchanged; the
|
|
@@ -3269,7 +3229,7 @@ var CLAUDE_CODE_ADDENDUM = [
|
|
|
3269
3229
|
"`mcp__cabane__finalize_upload`, and `mcp__cabane__mint_render_token`; the turn tools are",
|
|
3270
3230
|
"`mcp__cabane_companion__ask`, `mcp__cabane_companion__reply_to`,",
|
|
3271
3231
|
"`mcp__cabane_companion__wake_me`,",
|
|
3272
|
-
"`mcp__cabane_companion__send`,
|
|
3232
|
+
"`mcp__cabane_companion__send`, and",
|
|
3273
3233
|
"`mcp__cabane_companion__skip_turn`. Where this prompt names a tool by its bare verb, call",
|
|
3274
3234
|
"the prefixed form from your tool list. Your harness's own tools (shell, local file",
|
|
3275
3235
|
"read/edit) keep their ordinary names and act on the local machine only."
|
|
@@ -3381,7 +3341,7 @@ function decideResume(stored, currentCwd) {
|
|
|
3381
3341
|
|
|
3382
3342
|
// packages/agent-runtime/src/claude-code/options.ts
|
|
3383
3343
|
var CABANE_MCP_SERVER = "cabane";
|
|
3384
|
-
var
|
|
3344
|
+
var ACTIVE_CONVERSATION_HEADER = "x-cabane-active-conversation";
|
|
3385
3345
|
async function preToolUseHook(input) {
|
|
3386
3346
|
const toolInput = "tool_input" in input && input.tool_input && typeof input.tool_input === "object" ? input.tool_input : {};
|
|
3387
3347
|
const toolName = "tool_name" in input ? input.tool_name : void 0;
|
|
@@ -3418,7 +3378,7 @@ function buildClaudeCodeOptions(req, augment) {
|
|
|
3418
3378
|
url: req.cabane.mcpUrl,
|
|
3419
3379
|
headers: {
|
|
3420
3380
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
3421
|
-
[
|
|
3381
|
+
[ACTIVE_CONVERSATION_HEADER]: req.cabane.activeConversationId
|
|
3422
3382
|
},
|
|
3423
3383
|
// Claude Code 2.1.x defers every MCP tool behind tool-search by default;
|
|
3424
3384
|
// for cabane — the always-needed core toolset — force it into the turn-1
|
|
@@ -4240,7 +4200,7 @@ function selectAdapter(registry, runtime) {
|
|
|
4240
4200
|
// packages/agent-runtime/src/opencode/addendum.ts
|
|
4241
4201
|
var OPENCODE_ADDENDUM = [
|
|
4242
4202
|
"Every tool here goes by its plain name: the workspace tool is `sdk`; the turn tools are",
|
|
4243
|
-
"`ask`, `reply_to`, `wake_me`, `send`, `
|
|
4203
|
+
"`ask`, `reply_to`, `wake_me`, `send`, `skip_turn`; the ancillary tools are",
|
|
4244
4204
|
"`list_workspaces`, `read_binary`, `upload`, `begin_upload`, `finalize_upload`,",
|
|
4245
4205
|
"`mint_render_token`; and the host tools are plain verbs too (`bash`, `read`, `edit`). Mind",
|
|
4246
4206
|
"the collision: a bare `read` or `edit` is the HOST tool, acting on the local machine \u2014 a",
|
|
@@ -4507,7 +4467,7 @@ function parseOpencodeModel(model) {
|
|
|
4507
4467
|
// packages/agent-runtime/src/opencode/run-spec.ts
|
|
4508
4468
|
var CABANE_MCP_SERVER2 = "cabane";
|
|
4509
4469
|
var TURN_CONTROL_MCP_SERVER = "cabane_companion";
|
|
4510
|
-
var
|
|
4470
|
+
var ACTIVE_CONVERSATION_HEADER2 = "x-cabane-active-conversation";
|
|
4511
4471
|
function buildRunSpec(req, resumeSessionId) {
|
|
4512
4472
|
const { policy, config } = req;
|
|
4513
4473
|
const directory = req.local.cwd ?? "";
|
|
@@ -4564,7 +4524,7 @@ function buildMcp(req) {
|
|
|
4564
4524
|
url: req.cabane.mcpUrl,
|
|
4565
4525
|
headers: {
|
|
4566
4526
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
4567
|
-
[
|
|
4527
|
+
[ACTIVE_CONVERSATION_HEADER2]: req.cabane.activeConversationId
|
|
4568
4528
|
},
|
|
4569
4529
|
enabled: true
|
|
4570
4530
|
};
|
|
@@ -4574,7 +4534,7 @@ function buildMcp(req) {
|
|
|
4574
4534
|
url: req.cabane.turnControlUrl,
|
|
4575
4535
|
headers: {
|
|
4576
4536
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
4577
|
-
[
|
|
4537
|
+
[ACTIVE_CONVERSATION_HEADER2]: req.cabane.activeConversationId
|
|
4578
4538
|
},
|
|
4579
4539
|
enabled: true
|
|
4580
4540
|
};
|
|
@@ -5217,7 +5177,7 @@ var CODEX_ADDENDUM = [
|
|
|
5217
5177
|
"the deferred-tool inventory and invoke that exact qualified name; never conclude the SDK is",
|
|
5218
5178
|
"absent without attempting discovery and invocation, and if a call fails, report the recorded",
|
|
5219
5179
|
"error. The turn tools are `mcp__cabane_companion__ask` / `reply_to` / `wake_me` /",
|
|
5220
|
-
"`send` / `
|
|
5180
|
+
"`send` / `skip_turn`, and the ancillary tools",
|
|
5221
5181
|
"`mcp__cabane__list_workspaces` /",
|
|
5222
5182
|
"`read_binary` / `upload` / `begin_upload` / `finalize_upload` / `mint_render_token` \u2014 all",
|
|
5223
5183
|
"possibly deferred too. There is no Cabane `read`/`write`/`search` tool \u2014 those are",
|
|
@@ -5581,7 +5541,7 @@ function parseCodexModel(model) {
|
|
|
5581
5541
|
// packages/agent-runtime/src/codex/run-spec.ts
|
|
5582
5542
|
var CABANE_MCP_SERVER3 = "cabane";
|
|
5583
5543
|
var TURN_CONTROL_MCP_SERVER2 = "cabane_companion";
|
|
5584
|
-
var
|
|
5544
|
+
var ACTIVE_CONVERSATION_HEADER3 = "x-cabane-active-conversation";
|
|
5585
5545
|
var ENV_ENVELOPE_KEYS = [
|
|
5586
5546
|
"CABANE_PLAYGROUND",
|
|
5587
5547
|
"CABANE_PLAYGROUND_BIN",
|
|
@@ -5665,7 +5625,7 @@ function buildConfig(req, baseInstructionsFile) {
|
|
|
5665
5625
|
url: req.cabane.mcpUrl,
|
|
5666
5626
|
http_headers: {
|
|
5667
5627
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
5668
|
-
[
|
|
5628
|
+
[ACTIVE_CONVERSATION_HEADER3]: req.cabane.activeConversationId
|
|
5669
5629
|
},
|
|
5670
5630
|
default_tools_approval_mode: "approve"
|
|
5671
5631
|
};
|
|
@@ -5674,7 +5634,7 @@ function buildConfig(req, baseInstructionsFile) {
|
|
|
5674
5634
|
url: req.cabane.turnControlUrl,
|
|
5675
5635
|
http_headers: {
|
|
5676
5636
|
Authorization: `Bearer ${req.cabane.bearer}`,
|
|
5677
|
-
[
|
|
5637
|
+
[ACTIVE_CONVERSATION_HEADER3]: req.cabane.activeConversationId
|
|
5678
5638
|
},
|
|
5679
5639
|
default_tools_approval_mode: "approve"
|
|
5680
5640
|
};
|
|
@@ -6728,8 +6688,6 @@ var ASK_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${ASK_TOOL}`;
|
|
|
6728
6688
|
var REPLY_TO_TOOL = "reply_to";
|
|
6729
6689
|
var REPLY_TO_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${REPLY_TO_TOOL}`;
|
|
6730
6690
|
var MAX_ASK_ITEMS = 20;
|
|
6731
|
-
var SUB_AGENT_TOOL = "sub_agent";
|
|
6732
|
-
var SUB_AGENT_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${SUB_AGENT_TOOL}`;
|
|
6733
6691
|
var WAKE_ME_TOOL = "wake_me";
|
|
6734
6692
|
var WAKE_ME_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${WAKE_ME_TOOL}`;
|
|
6735
6693
|
var CANCEL_WAKE_TOOL = "cancel_wake";
|
|
@@ -6768,8 +6726,8 @@ function resolveDeclaredReplyField(input) {
|
|
|
6768
6726
|
);
|
|
6769
6727
|
const askRaised = Boolean(input.askState.targetUserId);
|
|
6770
6728
|
const wakeArmed = input.wakeState.afterSeconds !== null || input.wakeState.at !== null;
|
|
6771
|
-
if (outwardSend || askRaised || wakeArmed
|
|
6772
|
-
return { answersMessageId: input.owedReplyMessageId };
|
|
6729
|
+
if (outwardSend || askRaised || wakeArmed) return {};
|
|
6730
|
+
return { answersMessageId: input.owedReplyMessageId, answersAutoDeclared: true };
|
|
6773
6731
|
}
|
|
6774
6732
|
function wakeCommitField(state) {
|
|
6775
6733
|
if (state.cancelled) return { wake: { cancel: true } };
|
|
@@ -6783,7 +6741,7 @@ function wakeCommitField(state) {
|
|
|
6783
6741
|
}
|
|
6784
6742
|
};
|
|
6785
6743
|
}
|
|
6786
|
-
function createTurnControlMcpServer(sendState, skipState, askState,
|
|
6744
|
+
function createTurnControlMcpServer(sendState, skipState, askState, wakeState, replyState, controlOrder) {
|
|
6787
6745
|
return createSdkMcpServer({
|
|
6788
6746
|
name: COMPANION_LOCAL_MCP_SERVER,
|
|
6789
6747
|
version: "0.0.0",
|
|
@@ -6919,42 +6877,6 @@ function createTurnControlMcpServer(sendState, skipState, askState, subAgentCrea
|
|
|
6919
6877
|
{ annotations: { readOnlyHint: true, openWorldHint: false }, alwaysLoad: true }
|
|
6920
6878
|
)
|
|
6921
6879
|
] : [],
|
|
6922
|
-
...subAgentCreate ? [
|
|
6923
|
-
tool(
|
|
6924
|
-
SUB_AGENT_TOOL,
|
|
6925
|
-
"Ask into a child conversation \u2014 a bounded question or piece of work whose ANSWER returns to you automatically. This creates the child and dispatches its agent now, returning the child's `conversationId` immediately; the answer lands LATER as a message in THIS conversation, and you're woken once every child you asked this turn has answered. So DON'T wait: after asking, finish whatever else this turn can do and end your turn (never poll the child with reads in a loop \u2014 the wake is automatic and it carries the answer). Parallel fan-out = call this N times in one turn (they run concurrently; ONE wake carrying all the answers); series = one call per turn. `prompt` is the ask itself \u2014 write it as a request that expects an answer back, not a role you are assigning or a job description: self-contained (the child starts fresh, with only this prompt), and say what shape the answer should take. `agentId` (optional) asks a PEER instead of yourself \u2014 same mechanics, a different mind (use for capability/context you lack); default (self) is a fresh context window of you. `title` (optional) names the child thread (answers link it, so a legible title helps). A single child buys no wall-clock time (you idle either way) \u2014 it pays when the callee has capability/context you lack, or to isolate a big read from your own session; the real win is fan-out. Don't ask a child for a lookup you can do in-turn with your own tools. The answer returns to YOU to act on \u2014 reach for it when you're the consumer of the output, not as a way to notify a human: if a person just wants to read the result, dispatch a plain (no-return) conversation and link it instead.",
|
|
6926
|
-
{
|
|
6927
|
-
prompt: z13.string().min(1).max(65536).describe(
|
|
6928
|
-
"The ask \u2014 a self-contained request that expects an answer back (the child starts fresh: only this prompt + the thread it lands in). Say what shape the answer should take."
|
|
6929
|
-
),
|
|
6930
|
-
agentId: z13.string().uuid().optional().describe(
|
|
6931
|
-
"Optional peer to run the sub-agent as (a workspace agent id, from your turn context's roster); omit to spawn yourself with a fresh context window."
|
|
6932
|
-
),
|
|
6933
|
-
title: z13.string().max(200).optional().describe("Optional title for the child thread (result chips link it).")
|
|
6934
|
-
},
|
|
6935
|
-
async (args) => {
|
|
6936
|
-
const result = await subAgentCreate(args);
|
|
6937
|
-
if (!result.ok) {
|
|
6938
|
-
return {
|
|
6939
|
-
content: [{ type: "text", text: result.error }],
|
|
6940
|
-
isError: true
|
|
6941
|
-
};
|
|
6942
|
-
}
|
|
6943
|
-
return {
|
|
6944
|
-
content: [
|
|
6945
|
-
{
|
|
6946
|
-
type: "text",
|
|
6947
|
-
text: JSON.stringify({
|
|
6948
|
-
conversationId: result.conversationId,
|
|
6949
|
-
note: "Asked. Don't wait for it \u2014 finish what else this turn can do, then end your turn; the answer posts back here and you're woken once every child you asked this turn has answered."
|
|
6950
|
-
})
|
|
6951
|
-
}
|
|
6952
|
-
]
|
|
6953
|
-
};
|
|
6954
|
-
},
|
|
6955
|
-
{ annotations: { readOnlyHint: false, openWorldHint: true }, alwaysLoad: true }
|
|
6956
|
-
)
|
|
6957
|
-
] : [],
|
|
6958
6880
|
...wakeState ? [
|
|
6959
6881
|
tool(
|
|
6960
6882
|
WAKE_ME_TOOL,
|
|
@@ -7494,7 +7416,10 @@ var TurnCommitter = class {
|
|
|
7494
7416
|
}
|
|
7495
7417
|
// The declared reply: explicit `reply_to` first, else the runtime's own
|
|
7496
7418
|
// declaration for the turn that just answers — see
|
|
7497
|
-
// `resolveDeclaredReplyField` for the whole rule and its reasons.
|
|
7419
|
+
// `resolveDeclaredReplyField` for the whole rule and its reasons. CT1224: an
|
|
7420
|
+
// auto-declaration is flagged as such on the wire, because the server drops it
|
|
7421
|
+
// when the turn also opened a conversation (a fan-out is mid-arc). An explicit
|
|
7422
|
+
// `reply_to` carries no flag and always stands.
|
|
7498
7423
|
answersField(kind) {
|
|
7499
7424
|
return resolveDeclaredReplyField({
|
|
7500
7425
|
kind,
|
|
@@ -7503,8 +7428,7 @@ var TurnCommitter = class {
|
|
|
7503
7428
|
replyState: this.deps.replyState,
|
|
7504
7429
|
sendState: this.deps.sendState,
|
|
7505
7430
|
askState: this.deps.askState,
|
|
7506
|
-
wakeState: this.deps.wakeState
|
|
7507
|
-
spawnedSubAgent: this.deps.spawnedSubAgent()
|
|
7431
|
+
wakeState: this.deps.wakeState
|
|
7508
7432
|
});
|
|
7509
7433
|
}
|
|
7510
7434
|
// Carry both halves of the addressed send. The server writes `dispatchBody`
|
|
@@ -7625,17 +7549,6 @@ function describeErrorBody(body) {
|
|
|
7625
7549
|
if (text.length === 0) return void 0;
|
|
7626
7550
|
return text.length > ERROR_BODY_LOG_CAP ? `${text.slice(0, ERROR_BODY_LOG_CAP)}\u2026` : text;
|
|
7627
7551
|
}
|
|
7628
|
-
function describeSubAgentError(status, body) {
|
|
7629
|
-
const code = body && typeof body === "object" && "error" in body ? String(body.error) : void 0;
|
|
7630
|
-
switch (code) {
|
|
7631
|
-
case "dispatch_agent_not_found":
|
|
7632
|
-
return "sub_agent: no live agent in this workspace matches that `agentId`. Check the roster in your turn context, or omit `agentId` to spawn yourself.";
|
|
7633
|
-
case "dispatch_ask_requires_dispatch":
|
|
7634
|
-
return `sub_agent: the spawn was rejected (${code}). This is a turn-context problem, not something to retry blindly \u2014 report it rather than looping.`;
|
|
7635
|
-
default:
|
|
7636
|
-
return `sub_agent: the spawn failed (${code ?? `HTTP ${status}`}).`;
|
|
7637
|
-
}
|
|
7638
|
-
}
|
|
7639
7552
|
var Dispatcher = class {
|
|
7640
7553
|
constructor(opts) {
|
|
7641
7554
|
this.opts = opts;
|
|
@@ -7998,36 +7911,10 @@ ${reason}`,
|
|
|
7998
7911
|
const wakeState = createWakeState();
|
|
7999
7912
|
const replyState = createReplyState();
|
|
8000
7913
|
const controlOrder = createTurnControlOrder();
|
|
8001
|
-
let spawnedSubAgent = false;
|
|
8002
|
-
const subAgentCreate = async (args) => {
|
|
8003
|
-
const dispatchTarget = args.agentId ?? payload.agentId;
|
|
8004
|
-
const projectId = turnContext.conversation.projectId;
|
|
8005
|
-
const { status, body } = await this.opts.api.createSubAgentConversation(
|
|
8006
|
-
workspaceId,
|
|
8007
|
-
payload.conversationId,
|
|
8008
|
-
turnContext.turnToken ?? this.opts.credential,
|
|
8009
|
-
{
|
|
8010
|
-
...projectId ? { projectId } : {},
|
|
8011
|
-
...args.title ? { title: args.title } : {},
|
|
8012
|
-
body: args.prompt,
|
|
8013
|
-
dispatch: dispatchTarget,
|
|
8014
|
-
dispatchAsk: true,
|
|
8015
|
-
parentConversationId: payload.conversationId
|
|
8016
|
-
}
|
|
8017
|
-
);
|
|
8018
|
-
if (status >= 400) return { ok: false, error: describeSubAgentError(status, body) };
|
|
8019
|
-
const conversationId = body?.conversation?.id;
|
|
8020
|
-
if (!conversationId) {
|
|
8021
|
-
return { ok: false, error: "sub_agent: the spawn returned no conversation id." };
|
|
8022
|
-
}
|
|
8023
|
-
spawnedSubAgent = true;
|
|
8024
|
-
return { ok: true, conversationId };
|
|
8025
|
-
};
|
|
8026
7914
|
const turnControlServer = createTurnControlMcpServer(
|
|
8027
7915
|
sendState,
|
|
8028
7916
|
skipState,
|
|
8029
7917
|
askState,
|
|
8030
|
-
subAgentCreate,
|
|
8031
7918
|
wakeState,
|
|
8032
7919
|
replyState,
|
|
8033
7920
|
controlOrder
|
|
@@ -8163,8 +8050,7 @@ ${reason}`,
|
|
|
8163
8050
|
replyState,
|
|
8164
8051
|
// The ledger-derived owed reply, for the runtime's own declaration when
|
|
8165
8052
|
// the agent doesn't call `reply_to` (resolveDeclaredReplyField).
|
|
8166
|
-
owedReplyMessageId: turnContext.owedReplyMessageId ?? null
|
|
8167
|
-
spawnedSubAgent: () => spawnedSubAgent
|
|
8053
|
+
owedReplyMessageId: turnContext.owedReplyMessageId ?? null
|
|
8168
8054
|
});
|
|
8169
8055
|
const usesHttpTurnControl = turnRuntime === "codex" || turnRuntime === "opencode";
|
|
8170
8056
|
let turnControlIntentFetched = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.67",
|
|
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",
|