@cabane/companion 0.6.196 → 0.6.198
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/README.md +2 -2
- package/dist/cli.js +18 -11
- package/dist/runtime.js +18 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The Cabane Companion runs your Cabane agents on your own device. Install it, pas
|
|
|
4
4
|
|
|
5
5
|
It drives a **harness** you already have signed in: [Claude Code](https://www.npmjs.com/package/@anthropic-ai/claude-code), [Codex](https://developers.openai.com/codex/cli) or [OpenCode](https://opencode.ai). The Companion never installs a harness and never runs a sign-in; it finds the ones that are usable and reports them.
|
|
6
6
|
|
|
7
|
-
Step-by-step guides with screenshots are
|
|
7
|
+
Step-by-step guides with screenshots are at [cabane.ai/docs](https://cabane.ai/docs): [Connect your computer](https://cabane.ai/docs/connect-your-computer) to install and pair, [Create an agent](https://cabane.ai/docs/create-an-agent) to give an agent a device, and [When an agent doesn't answer](https://cabane.ai/docs/when-an-agent-doesnt-answer) when one stays silent. This README is the reference for the device side.
|
|
8
8
|
|
|
9
9
|
## The model
|
|
10
10
|
|
|
@@ -131,7 +131,7 @@ Cabane has no sandbox setting of its own. A harness's own sandbox settings restr
|
|
|
131
131
|
|
|
132
132
|
So where agents run is your choice, and there are two ways:
|
|
133
133
|
|
|
134
|
-
- **The Companion in a container or VM**, recommended for almost everyone: the Companion and the harness together in Docker, OrbStack or a VM, sharing only the folders and credentials they need. Agents keep a full shell to work and code in, still with no approvals, while the container or VM limits what they reach to what you put in, MCP servers, hooks and the harness process included. If you haven't set one up before, ask Claude to help;
|
|
134
|
+
- **The Companion in a container or VM**, recommended for almost everyone: the Companion and the harness together in Docker, OrbStack or a VM, sharing only the folders and credentials they need. Agents keep a full shell to work and code in, still with no approvals, while the container or VM limits what they reach to what you put in, MCP servers, hooks and the harness process included. If you haven't set one up before, ask Claude to help; [Common device patterns](https://cabane.ai/docs/common-device-patterns) walks through one with Docker.
|
|
135
135
|
- **Directly on your device**, with the harness's own sandbox set to taste. It restricts particular tools, and each harness draws a different line.
|
|
136
136
|
|
|
137
137
|
Running directly, confining agents is set in each harness's own file, and governs your own sessions there as well as every agent on the device:
|
package/dist/cli.js
CHANGED
|
@@ -1682,7 +1682,7 @@ var init_api = __esm({
|
|
|
1682
1682
|
// Single-shot, NEVER retried: a failed renewal has no answer worth replaying
|
|
1683
1683
|
// and the next tick asks again; retrying stretches one tick over the next.
|
|
1684
1684
|
//
|
|
1685
|
-
//
|
|
1685
|
+
// Only the server SAYING the turn is over may stop a
|
|
1686
1686
|
// running loop; a 500, a dropped connection, an expired token or an
|
|
1687
1687
|
// unparseable body says nothing about the turn, and reading it as death would
|
|
1688
1688
|
// kill healthy turns on every API hiccup. Fixed here, at the boundary, so no
|
|
@@ -1706,8 +1706,8 @@ var init_api = __esm({
|
|
|
1706
1706
|
}
|
|
1707
1707
|
);
|
|
1708
1708
|
return res?.state === "running" || res?.state === "ended" ? res.state : "unknown";
|
|
1709
|
-
} catch {
|
|
1710
|
-
return "unknown";
|
|
1709
|
+
} catch (err) {
|
|
1710
|
+
return isTurnEndedRefusal(err) ? "ended" : "unknown";
|
|
1711
1711
|
}
|
|
1712
1712
|
}
|
|
1713
1713
|
// Flips `active_run_started_at` and optionally captures the SDK session
|
|
@@ -10557,8 +10557,8 @@ var init_dispatcher = __esm({
|
|
|
10557
10557
|
this.aborts = opts.aborts ?? /* @__PURE__ */ new Map();
|
|
10558
10558
|
}
|
|
10559
10559
|
opts;
|
|
10560
|
-
// (conversation|agent) → per-
|
|
10561
|
-
//
|
|
10560
|
+
// (conversation|agent) → per-turn controllers. A delayed cancel for an old
|
|
10561
|
+
// turn must never reach its successor.
|
|
10562
10562
|
aborts;
|
|
10563
10563
|
// Pairs already told their session couldn't be saved: the notice is once per
|
|
10564
10564
|
// pair, while the rejection repeats every turn.
|
|
@@ -10609,19 +10609,22 @@ var init_dispatcher = __esm({
|
|
|
10609
10609
|
{ turnId: opts.turnId ?? randomUUID4(), resumed: opts.resumed === true }
|
|
10610
10610
|
);
|
|
10611
10611
|
}
|
|
10612
|
-
// Cancel
|
|
10613
|
-
|
|
10614
|
-
cancel(conversationId, agentId) {
|
|
10612
|
+
// Cancel the named turn. An older server omits turnId and cancels the pair.
|
|
10613
|
+
cancel(conversationId, agentId, turnId) {
|
|
10615
10614
|
const key = runKey(conversationId, agentId);
|
|
10616
10615
|
const pair = this.aborts.get(key);
|
|
10617
10616
|
if (!pair || pair.size === 0) return false;
|
|
10618
|
-
|
|
10617
|
+
const controllers = turnId === void 0 ? [...pair.values()] : [pair.get(turnId)];
|
|
10618
|
+
let aborted = false;
|
|
10619
|
+
for (const ac of controllers) {
|
|
10620
|
+
if (!ac) continue;
|
|
10621
|
+
aborted = true;
|
|
10619
10622
|
try {
|
|
10620
10623
|
ac.abort();
|
|
10621
10624
|
} catch {
|
|
10622
10625
|
}
|
|
10623
10626
|
}
|
|
10624
|
-
return
|
|
10627
|
+
return aborted;
|
|
10625
10628
|
}
|
|
10626
10629
|
// Abort every run in flight in THIS process, whatever its conversation. For
|
|
10627
10630
|
// an agent taken off this device: nothing it is still running here is
|
|
@@ -14918,7 +14921,11 @@ var CompanionSupervisor = class {
|
|
|
14918
14921
|
};
|
|
14919
14922
|
const agent2 = wr.agents.get(payload2.agentId);
|
|
14920
14923
|
if (agent2) {
|
|
14921
|
-
const aborted = agent2.dispatcher.cancel(
|
|
14924
|
+
const aborted = agent2.dispatcher.cancel(
|
|
14925
|
+
payload2.conversationId,
|
|
14926
|
+
payload2.agentId,
|
|
14927
|
+
payload2.turnId
|
|
14928
|
+
);
|
|
14922
14929
|
if (aborted) {
|
|
14923
14930
|
this.log.debug(
|
|
14924
14931
|
{
|
package/dist/runtime.js
CHANGED
|
@@ -2110,7 +2110,7 @@ var CabaneApi = class {
|
|
|
2110
2110
|
// Single-shot, NEVER retried: a failed renewal has no answer worth replaying
|
|
2111
2111
|
// and the next tick asks again; retrying stretches one tick over the next.
|
|
2112
2112
|
//
|
|
2113
|
-
//
|
|
2113
|
+
// Only the server SAYING the turn is over may stop a
|
|
2114
2114
|
// running loop; a 500, a dropped connection, an expired token or an
|
|
2115
2115
|
// unparseable body says nothing about the turn, and reading it as death would
|
|
2116
2116
|
// kill healthy turns on every API hiccup. Fixed here, at the boundary, so no
|
|
@@ -2134,8 +2134,8 @@ var CabaneApi = class {
|
|
|
2134
2134
|
}
|
|
2135
2135
|
);
|
|
2136
2136
|
return res?.state === "running" || res?.state === "ended" ? res.state : "unknown";
|
|
2137
|
-
} catch {
|
|
2138
|
-
return "unknown";
|
|
2137
|
+
} catch (err) {
|
|
2138
|
+
return isTurnEndedRefusal(err) ? "ended" : "unknown";
|
|
2139
2139
|
}
|
|
2140
2140
|
}
|
|
2141
2141
|
// Flips `active_run_started_at` and optionally captures the SDK session
|
|
@@ -8257,8 +8257,8 @@ var Dispatcher = class {
|
|
|
8257
8257
|
this.aborts = opts.aborts ?? /* @__PURE__ */ new Map();
|
|
8258
8258
|
}
|
|
8259
8259
|
opts;
|
|
8260
|
-
// (conversation|agent) → per-
|
|
8261
|
-
//
|
|
8260
|
+
// (conversation|agent) → per-turn controllers. A delayed cancel for an old
|
|
8261
|
+
// turn must never reach its successor.
|
|
8262
8262
|
aborts;
|
|
8263
8263
|
// Pairs already told their session couldn't be saved: the notice is once per
|
|
8264
8264
|
// pair, while the rejection repeats every turn.
|
|
@@ -8309,19 +8309,22 @@ var Dispatcher = class {
|
|
|
8309
8309
|
{ turnId: opts.turnId ?? randomUUID4(), resumed: opts.resumed === true }
|
|
8310
8310
|
);
|
|
8311
8311
|
}
|
|
8312
|
-
// Cancel
|
|
8313
|
-
|
|
8314
|
-
cancel(conversationId, agentId) {
|
|
8312
|
+
// Cancel the named turn. An older server omits turnId and cancels the pair.
|
|
8313
|
+
cancel(conversationId, agentId, turnId) {
|
|
8315
8314
|
const key = runKey(conversationId, agentId);
|
|
8316
8315
|
const pair = this.aborts.get(key);
|
|
8317
8316
|
if (!pair || pair.size === 0) return false;
|
|
8318
|
-
|
|
8317
|
+
const controllers = turnId === void 0 ? [...pair.values()] : [pair.get(turnId)];
|
|
8318
|
+
let aborted = false;
|
|
8319
|
+
for (const ac of controllers) {
|
|
8320
|
+
if (!ac) continue;
|
|
8321
|
+
aborted = true;
|
|
8319
8322
|
try {
|
|
8320
8323
|
ac.abort();
|
|
8321
8324
|
} catch {
|
|
8322
8325
|
}
|
|
8323
8326
|
}
|
|
8324
|
-
return
|
|
8327
|
+
return aborted;
|
|
8325
8328
|
}
|
|
8326
8329
|
// Abort every run in flight in THIS process, whatever its conversation. For
|
|
8327
8330
|
// an agent taken off this device: nothing it is still running here is
|
|
@@ -10528,7 +10531,11 @@ var CompanionSupervisor = class {
|
|
|
10528
10531
|
};
|
|
10529
10532
|
const agent2 = wr.agents.get(payload2.agentId);
|
|
10530
10533
|
if (agent2) {
|
|
10531
|
-
const aborted = agent2.dispatcher.cancel(
|
|
10534
|
+
const aborted = agent2.dispatcher.cancel(
|
|
10535
|
+
payload2.conversationId,
|
|
10536
|
+
payload2.agentId,
|
|
10537
|
+
payload2.turnId
|
|
10538
|
+
);
|
|
10532
10539
|
if (aborted) {
|
|
10533
10540
|
this.log.debug(
|
|
10534
10541
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.198",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect an 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",
|