@cabane/companion 0.6.12 → 0.6.13
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 +59 -19
- package/dist/runtime.js +59 -19
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6295,6 +6295,8 @@ var SUB_AGENT_TOOL = "sub_agent";
|
|
|
6295
6295
|
var SUB_AGENT_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${SUB_AGENT_TOOL}`;
|
|
6296
6296
|
var WAKE_ME_TOOL = "wake_me";
|
|
6297
6297
|
var WAKE_ME_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${WAKE_ME_TOOL}`;
|
|
6298
|
+
var CANCEL_WAKE_TOOL = "cancel_wake";
|
|
6299
|
+
var CANCEL_WAKE_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${CANCEL_WAKE_TOOL}`;
|
|
6298
6300
|
function createSummonState() {
|
|
6299
6301
|
return { agentId: null };
|
|
6300
6302
|
}
|
|
@@ -6305,7 +6307,19 @@ function createAskState() {
|
|
|
6305
6307
|
return { targetUserId: null, question: null, headline: null, options: null, questions: null };
|
|
6306
6308
|
}
|
|
6307
6309
|
function createWakeState() {
|
|
6308
|
-
return { afterSeconds: null, at: null, note: null };
|
|
6310
|
+
return { afterSeconds: null, at: null, note: null, cancelled: false };
|
|
6311
|
+
}
|
|
6312
|
+
function wakeCommitField(state) {
|
|
6313
|
+
if (state.cancelled) return { wake: { cancel: true } };
|
|
6314
|
+
const { afterSeconds, at, note } = state;
|
|
6315
|
+
if (!note || afterSeconds === null && at === null) return {};
|
|
6316
|
+
return {
|
|
6317
|
+
wake: {
|
|
6318
|
+
...afterSeconds !== null ? { afterSeconds } : {},
|
|
6319
|
+
...at !== null ? { at } : {},
|
|
6320
|
+
note
|
|
6321
|
+
}
|
|
6322
|
+
};
|
|
6309
6323
|
}
|
|
6310
6324
|
function createSummonMcpServer(summonState, skipState, askState, subAgentCreate, wakeState) {
|
|
6311
6325
|
return createSdkMcpServer({
|
|
@@ -6492,6 +6506,7 @@ function createSummonMcpServer(summonState, skipState, askState, subAgentCreate,
|
|
|
6492
6506
|
wakeState.afterSeconds = args.afterSeconds ?? null;
|
|
6493
6507
|
wakeState.at = args.at ?? null;
|
|
6494
6508
|
wakeState.note = args.note;
|
|
6509
|
+
wakeState.cancelled = false;
|
|
6495
6510
|
return {
|
|
6496
6511
|
content: [
|
|
6497
6512
|
{
|
|
@@ -6505,6 +6520,31 @@ function createSummonMcpServer(summonState, skipState, askState, subAgentCreate,
|
|
|
6505
6520
|
};
|
|
6506
6521
|
},
|
|
6507
6522
|
{ annotations: { readOnlyHint: true, openWorldHint: false }, alwaysLoad: true }
|
|
6523
|
+
),
|
|
6524
|
+
// CT990: the inverse. No arguments — it can only ever stand down the
|
|
6525
|
+
// caller's own wake in the conversation it's holding a turn in.
|
|
6526
|
+
tool(
|
|
6527
|
+
CANCEL_WAKE_TOOL,
|
|
6528
|
+
'Stand down your own wake \u2014 the inverse of `wake_me`. No arguments: it means "when this turn settles, leave no wake armed for me in this conversation." Reach for it when the thing you armed a wake to check has already happened, or the work it was watching is over \u2014 an armed wake you no longer need fires into a turn with nothing to do, and a supervision loop with no off switch is one you can only end by leaving it running. Whether you have one armed is printed in your turn context ("Wake armed: \u2026"); calling this with nothing armed is a clean no-op, no error. It writes the same per-turn slot as `wake_me`, so the two are last-call-wins against each other: `cancel_wake` then `wake_me` leaves the NEW wake armed, `wake_me` then `cancel_wake` leaves nothing armed. Like `wake_me` it takes effect when the turn SETTLES, not now. It reaches only your own wake in this conversation \u2014 never a peer\'s, and never a recurring schedule (those are `cabane.schedules.*`).',
|
|
6529
|
+
{},
|
|
6530
|
+
async () => {
|
|
6531
|
+
wakeState.cancelled = true;
|
|
6532
|
+
wakeState.afterSeconds = null;
|
|
6533
|
+
wakeState.at = null;
|
|
6534
|
+
wakeState.note = null;
|
|
6535
|
+
return {
|
|
6536
|
+
content: [
|
|
6537
|
+
{
|
|
6538
|
+
type: "text",
|
|
6539
|
+
text: JSON.stringify({
|
|
6540
|
+
cancelled: true,
|
|
6541
|
+
note: "Recorded. Any wake you have armed in this conversation is stood down when this turn ends. If you had none, nothing happens."
|
|
6542
|
+
})
|
|
6543
|
+
}
|
|
6544
|
+
]
|
|
6545
|
+
};
|
|
6546
|
+
},
|
|
6547
|
+
{ annotations: { readOnlyHint: true, openWorldHint: false }, alwaysLoad: true }
|
|
6508
6548
|
)
|
|
6509
6549
|
] : []
|
|
6510
6550
|
]
|
|
@@ -6941,16 +6981,13 @@ var TurnCommitter = class {
|
|
|
6941
6981
|
// null = no wake this turn → attach nothing). The server computes `fire_at` and
|
|
6942
6982
|
// arms the CT441 schedule. Kept independent of summon/ask — a turn could
|
|
6943
6983
|
// conceivably ask AND arm a wake.
|
|
6984
|
+
//
|
|
6985
|
+
// CT990: the field now carries three states, and the third can't be spelled by
|
|
6986
|
+
// absence — attaching NOTHING means "leave the standing wake alone", which is
|
|
6987
|
+
// exactly what a stand-down must not do. So a `cancel_wake` turn attaches the
|
|
6988
|
+
// discriminated `{ cancel: true }` payload instead.
|
|
6944
6989
|
wakeField() {
|
|
6945
|
-
|
|
6946
|
-
if (!note || afterSeconds === null && at === null) return {};
|
|
6947
|
-
return {
|
|
6948
|
-
wake: {
|
|
6949
|
-
...afterSeconds !== null ? { afterSeconds } : {},
|
|
6950
|
-
...at !== null ? { at } : {},
|
|
6951
|
-
note
|
|
6952
|
-
}
|
|
6953
|
-
};
|
|
6990
|
+
return wakeCommitField(this.deps.wakeState);
|
|
6954
6991
|
}
|
|
6955
6992
|
};
|
|
6956
6993
|
|
|
@@ -7627,9 +7664,17 @@ ${reason}`,
|
|
|
7627
7664
|
}
|
|
7628
7665
|
}
|
|
7629
7666
|
if (intent.wake) {
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7667
|
+
if ("cancel" in intent.wake) {
|
|
7668
|
+
wakeState.cancelled = true;
|
|
7669
|
+
wakeState.afterSeconds = null;
|
|
7670
|
+
wakeState.at = null;
|
|
7671
|
+
wakeState.note = null;
|
|
7672
|
+
} else {
|
|
7673
|
+
wakeState.cancelled = false;
|
|
7674
|
+
wakeState.afterSeconds = intent.wake.afterSeconds ?? null;
|
|
7675
|
+
wakeState.at = intent.wake.at ?? null;
|
|
7676
|
+
wakeState.note = intent.wake.note;
|
|
7677
|
+
}
|
|
7633
7678
|
}
|
|
7634
7679
|
if (intent.summonAgentId) summonState.agentId = intent.summonAgentId;
|
|
7635
7680
|
if (intent.skipped) {
|
|
@@ -7720,12 +7765,7 @@ ${reason}`,
|
|
|
7720
7765
|
{ reason: skipState.reason, turnId, ok: okResult },
|
|
7721
7766
|
"agent skipped turn (skip_turn)"
|
|
7722
7767
|
);
|
|
7723
|
-
const {
|
|
7724
|
-
const skipWake = wakeNote && (wakeAfter !== null || wakeAt !== null) ? {
|
|
7725
|
-
...wakeAfter !== null ? { afterSeconds: wakeAfter } : {},
|
|
7726
|
-
...wakeAt !== null ? { at: wakeAt } : {},
|
|
7727
|
-
note: wakeNote
|
|
7728
|
-
} : void 0;
|
|
7768
|
+
const { wake: skipWake } = wakeCommitField(wakeState);
|
|
7729
7769
|
try {
|
|
7730
7770
|
await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
|
|
7731
7771
|
body: SKIPPED_MARKER_BODY,
|
package/dist/runtime.js
CHANGED
|
@@ -5948,6 +5948,8 @@ var SUB_AGENT_TOOL = "sub_agent";
|
|
|
5948
5948
|
var SUB_AGENT_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${SUB_AGENT_TOOL}`;
|
|
5949
5949
|
var WAKE_ME_TOOL = "wake_me";
|
|
5950
5950
|
var WAKE_ME_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${WAKE_ME_TOOL}`;
|
|
5951
|
+
var CANCEL_WAKE_TOOL = "cancel_wake";
|
|
5952
|
+
var CANCEL_WAKE_TOOL_NAME = `mcp__${COMPANION_LOCAL_MCP_SERVER}__${CANCEL_WAKE_TOOL}`;
|
|
5951
5953
|
function createSummonState() {
|
|
5952
5954
|
return { agentId: null };
|
|
5953
5955
|
}
|
|
@@ -5958,7 +5960,19 @@ function createAskState() {
|
|
|
5958
5960
|
return { targetUserId: null, question: null, headline: null, options: null, questions: null };
|
|
5959
5961
|
}
|
|
5960
5962
|
function createWakeState() {
|
|
5961
|
-
return { afterSeconds: null, at: null, note: null };
|
|
5963
|
+
return { afterSeconds: null, at: null, note: null, cancelled: false };
|
|
5964
|
+
}
|
|
5965
|
+
function wakeCommitField(state) {
|
|
5966
|
+
if (state.cancelled) return { wake: { cancel: true } };
|
|
5967
|
+
const { afterSeconds, at, note } = state;
|
|
5968
|
+
if (!note || afterSeconds === null && at === null) return {};
|
|
5969
|
+
return {
|
|
5970
|
+
wake: {
|
|
5971
|
+
...afterSeconds !== null ? { afterSeconds } : {},
|
|
5972
|
+
...at !== null ? { at } : {},
|
|
5973
|
+
note
|
|
5974
|
+
}
|
|
5975
|
+
};
|
|
5962
5976
|
}
|
|
5963
5977
|
function createSummonMcpServer(summonState, skipState, askState, subAgentCreate, wakeState) {
|
|
5964
5978
|
return createSdkMcpServer({
|
|
@@ -6145,6 +6159,7 @@ function createSummonMcpServer(summonState, skipState, askState, subAgentCreate,
|
|
|
6145
6159
|
wakeState.afterSeconds = args.afterSeconds ?? null;
|
|
6146
6160
|
wakeState.at = args.at ?? null;
|
|
6147
6161
|
wakeState.note = args.note;
|
|
6162
|
+
wakeState.cancelled = false;
|
|
6148
6163
|
return {
|
|
6149
6164
|
content: [
|
|
6150
6165
|
{
|
|
@@ -6158,6 +6173,31 @@ function createSummonMcpServer(summonState, skipState, askState, subAgentCreate,
|
|
|
6158
6173
|
};
|
|
6159
6174
|
},
|
|
6160
6175
|
{ annotations: { readOnlyHint: true, openWorldHint: false }, alwaysLoad: true }
|
|
6176
|
+
),
|
|
6177
|
+
// CT990: the inverse. No arguments — it can only ever stand down the
|
|
6178
|
+
// caller's own wake in the conversation it's holding a turn in.
|
|
6179
|
+
tool(
|
|
6180
|
+
CANCEL_WAKE_TOOL,
|
|
6181
|
+
'Stand down your own wake \u2014 the inverse of `wake_me`. No arguments: it means "when this turn settles, leave no wake armed for me in this conversation." Reach for it when the thing you armed a wake to check has already happened, or the work it was watching is over \u2014 an armed wake you no longer need fires into a turn with nothing to do, and a supervision loop with no off switch is one you can only end by leaving it running. Whether you have one armed is printed in your turn context ("Wake armed: \u2026"); calling this with nothing armed is a clean no-op, no error. It writes the same per-turn slot as `wake_me`, so the two are last-call-wins against each other: `cancel_wake` then `wake_me` leaves the NEW wake armed, `wake_me` then `cancel_wake` leaves nothing armed. Like `wake_me` it takes effect when the turn SETTLES, not now. It reaches only your own wake in this conversation \u2014 never a peer\'s, and never a recurring schedule (those are `cabane.schedules.*`).',
|
|
6182
|
+
{},
|
|
6183
|
+
async () => {
|
|
6184
|
+
wakeState.cancelled = true;
|
|
6185
|
+
wakeState.afterSeconds = null;
|
|
6186
|
+
wakeState.at = null;
|
|
6187
|
+
wakeState.note = null;
|
|
6188
|
+
return {
|
|
6189
|
+
content: [
|
|
6190
|
+
{
|
|
6191
|
+
type: "text",
|
|
6192
|
+
text: JSON.stringify({
|
|
6193
|
+
cancelled: true,
|
|
6194
|
+
note: "Recorded. Any wake you have armed in this conversation is stood down when this turn ends. If you had none, nothing happens."
|
|
6195
|
+
})
|
|
6196
|
+
}
|
|
6197
|
+
]
|
|
6198
|
+
};
|
|
6199
|
+
},
|
|
6200
|
+
{ annotations: { readOnlyHint: true, openWorldHint: false }, alwaysLoad: true }
|
|
6161
6201
|
)
|
|
6162
6202
|
] : []
|
|
6163
6203
|
]
|
|
@@ -6594,16 +6634,13 @@ var TurnCommitter = class {
|
|
|
6594
6634
|
// null = no wake this turn → attach nothing). The server computes `fire_at` and
|
|
6595
6635
|
// arms the CT441 schedule. Kept independent of summon/ask — a turn could
|
|
6596
6636
|
// conceivably ask AND arm a wake.
|
|
6637
|
+
//
|
|
6638
|
+
// CT990: the field now carries three states, and the third can't be spelled by
|
|
6639
|
+
// absence — attaching NOTHING means "leave the standing wake alone", which is
|
|
6640
|
+
// exactly what a stand-down must not do. So a `cancel_wake` turn attaches the
|
|
6641
|
+
// discriminated `{ cancel: true }` payload instead.
|
|
6597
6642
|
wakeField() {
|
|
6598
|
-
|
|
6599
|
-
if (!note || afterSeconds === null && at === null) return {};
|
|
6600
|
-
return {
|
|
6601
|
-
wake: {
|
|
6602
|
-
...afterSeconds !== null ? { afterSeconds } : {},
|
|
6603
|
-
...at !== null ? { at } : {},
|
|
6604
|
-
note
|
|
6605
|
-
}
|
|
6606
|
-
};
|
|
6643
|
+
return wakeCommitField(this.deps.wakeState);
|
|
6607
6644
|
}
|
|
6608
6645
|
};
|
|
6609
6646
|
|
|
@@ -7280,9 +7317,17 @@ ${reason}`,
|
|
|
7280
7317
|
}
|
|
7281
7318
|
}
|
|
7282
7319
|
if (intent.wake) {
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7320
|
+
if ("cancel" in intent.wake) {
|
|
7321
|
+
wakeState.cancelled = true;
|
|
7322
|
+
wakeState.afterSeconds = null;
|
|
7323
|
+
wakeState.at = null;
|
|
7324
|
+
wakeState.note = null;
|
|
7325
|
+
} else {
|
|
7326
|
+
wakeState.cancelled = false;
|
|
7327
|
+
wakeState.afterSeconds = intent.wake.afterSeconds ?? null;
|
|
7328
|
+
wakeState.at = intent.wake.at ?? null;
|
|
7329
|
+
wakeState.note = intent.wake.note;
|
|
7330
|
+
}
|
|
7286
7331
|
}
|
|
7287
7332
|
if (intent.summonAgentId) summonState.agentId = intent.summonAgentId;
|
|
7288
7333
|
if (intent.skipped) {
|
|
@@ -7373,12 +7418,7 @@ ${reason}`,
|
|
|
7373
7418
|
{ reason: skipState.reason, turnId, ok: okResult },
|
|
7374
7419
|
"agent skipped turn (skip_turn)"
|
|
7375
7420
|
);
|
|
7376
|
-
const {
|
|
7377
|
-
const skipWake = wakeNote && (wakeAfter !== null || wakeAt !== null) ? {
|
|
7378
|
-
...wakeAfter !== null ? { afterSeconds: wakeAfter } : {},
|
|
7379
|
-
...wakeAt !== null ? { at: wakeAt } : {},
|
|
7380
|
-
note: wakeNote
|
|
7381
|
-
} : void 0;
|
|
7421
|
+
const { wake: skipWake } = wakeCommitField(wakeState);
|
|
7382
7422
|
try {
|
|
7383
7423
|
await this.opts.api.postTurnMessage(workspaceId, payload.conversationId, {
|
|
7384
7424
|
body: SKIPPED_MARKER_BODY,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.13",
|
|
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",
|