baychat 0.36.0 → 0.38.0
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/.claude-plugin/plugin.json +1 -1
- package/dist/approval-wait.js +14 -0
- package/dist/approve-hook.js +71 -8
- package/dist/channel/channel-check.js +53 -0
- package/dist/channel/claude-link.js +9 -4
- package/dist/channel/feed.js +59 -5
- package/dist/channel/server.js +60 -12
- package/dist/cli-help.js +8 -1
- package/dist/launch/command.js +15 -4
- package/dist/launch/policy.js +12 -2
- package/dist/main.js +13 -0
- package/dist/notify-answer.js +196 -0
- package/dist/notify-hook.js +227 -0
- package/dist/notify-state.js +136 -0
- package/dist/protocol-content.js +1 -1
- package/dist/relay/launcher/handle.js +41 -0
- package/dist/relay/launcher/loop.js +9 -2
- package/hooks/hooks.json +25 -1
- package/package.json +1 -1
|
@@ -39,6 +39,9 @@ async function handleLaunch(launch, deps, io) {
|
|
|
39
39
|
return refused("NAME_INVALID");
|
|
40
40
|
if (launch.kind === "STOP")
|
|
41
41
|
return stopSession(launch, deps);
|
|
42
|
+
const agentProblem = agentStartRefusal(launch, deps);
|
|
43
|
+
if (agentProblem)
|
|
44
|
+
return agentProblem;
|
|
42
45
|
if ((0, acp_launch_1.isAcpLaunchRuntime)(launch.runtime))
|
|
43
46
|
return startAcp(launch, deps);
|
|
44
47
|
if (launch.runtime === "codex" && deps.codex && (0, codex_version_1.codexExecFallback)() && inDefaultBay(launch, deps)) {
|
|
@@ -58,6 +61,38 @@ async function handleLaunch(launch, deps, io) {
|
|
|
58
61
|
return { status: "STARTED" };
|
|
59
62
|
return watchForDevChannelWarning(launch, started.entry, deps, io);
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* An AGENT's start (Phase 4, docs/features/AUTOMATIONS_AND_PACKAGES.md §4.3–4.4), before anything
|
|
66
|
+
* else about it: undefined when it may go on.
|
|
67
|
+
*
|
|
68
|
+
* TWO KEYS. One that went through the owner's standing permission with no card ("agent-grant"),
|
|
69
|
+
* and a schedule's run ("schedule" — a standing permission too), needs the computer's own yes as
|
|
70
|
+
* well — the spot's `agentStarts`, which only `baychat launch add … --agent-starts` at this
|
|
71
|
+
* computer writes — or it is refused AGENT_STARTS_NOT_ALLOWED. A spot that no longer exists is
|
|
72
|
+
* left to `spotFor` (SPOT_UNKNOWN). A card-allowed start needs no flag.
|
|
73
|
+
*
|
|
74
|
+
* NEVER UNATTENDED. Any agent's start runs with its normal approval cards on the owner's phone:
|
|
75
|
+
* Claude Code with its approve hook, Codex only as the relay runs it (app-server, approvals on the
|
|
76
|
+
* phone) — never the Codex TUI with `--ask-for-approval never`, which a start in another Bay, or a
|
|
77
|
+
* relay without the Codex runner, would otherwise open. Those are refused as not offered yet.
|
|
78
|
+
*/
|
|
79
|
+
function agentStartRefusal(launch, deps) {
|
|
80
|
+
if (launch.startedBy === "person")
|
|
81
|
+
return undefined;
|
|
82
|
+
if (launch.startedBy === "agent-grant" || launch.startedBy === "schedule") {
|
|
83
|
+
const spot = deps.loadPolicy()?.spots.find((s) => s.id === launch.spotId);
|
|
84
|
+
if (spot && !spot.agentStarts) {
|
|
85
|
+
const who = launch.startedBy === "schedule" ? "schedules" : "agents";
|
|
86
|
+
deps.log(`launch ${launch.id}: refused, ${who} may not start sessions in "${spot.label}" without asking. Run \`baychat launch add <folder> --agent-starts\` here to allow it.`);
|
|
87
|
+
return refused("AGENT_STARTS_NOT_ALLOWED");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (launch.runtime === "codex" && !relayRunsCodex(launch, deps)) {
|
|
91
|
+
deps.log(`launch ${launch.id}: refused, an agent's or a schedule's Codex start runs only as the relay runs it, with approvals on the phone`);
|
|
92
|
+
return refused(inDefaultBay(launch, deps) ? "RUNTIME_NOT_YET" : "OTHER_BAY_NOT_YET");
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
61
96
|
/** The checks every START makes before anything runs: the spot, the runtime, the room, the folder. */
|
|
62
97
|
function spotFor(launch, deps) {
|
|
63
98
|
const spot = deps.loadPolicy()?.spots.find((s) => s.id === launch.spotId);
|
|
@@ -530,6 +565,10 @@ function parseLaunch(raw) {
|
|
|
530
565
|
const listen = l.listen === undefined || l.listen === "channel" ? "channel" : l.listen === "monitor" ? "monitor" : null;
|
|
531
566
|
if (!listen)
|
|
532
567
|
return null;
|
|
568
|
+
// Missing = an older server = a person's start. Anything unknown is malformed, never a person.
|
|
569
|
+
const startedBy = l.startedBy === undefined ? "person" : STARTED_BY.find((s) => s === l.startedBy);
|
|
570
|
+
if (!startedBy)
|
|
571
|
+
return null;
|
|
533
572
|
return {
|
|
534
573
|
id: l.id,
|
|
535
574
|
kind: l.kind,
|
|
@@ -545,8 +584,10 @@ function parseLaunch(raw) {
|
|
|
545
584
|
commandAuthorized: l.commandAuthorized === true,
|
|
546
585
|
// Only an explicit "any" on a STOP widens it; anything else is the launcher's own sessions.
|
|
547
586
|
...(l.kind === "STOP" && l.scope === "any" ? { scope: "any" } : {}),
|
|
587
|
+
startedBy,
|
|
548
588
|
};
|
|
549
589
|
}
|
|
590
|
+
const STARTED_BY = ["person", "agent-card", "agent-grant", "schedule"];
|
|
550
591
|
/**
|
|
551
592
|
* Which of this computer's Bay connections a launch joins on. `entry` is the MCP connection to
|
|
552
593
|
* name in the `/baychat` line — undefined when there is only one, so there is nothing to choose
|
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.deviceRequest = void 0;
|
|
36
|
+
exports.deviceRequest = exports.LAUNCH_LEVEL = void 0;
|
|
37
37
|
exports.startLauncher = startLauncher;
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const api_1 = require("../../api");
|
|
@@ -241,6 +241,13 @@ function realLoopDeps(auth, log, signal, { forgetSession, registeredSession, reg
|
|
|
241
241
|
: {}),
|
|
242
242
|
};
|
|
243
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* This relay's launch level (security sweep S4, 29 Oct): 1 = it honours `startedBy` and a folder's
|
|
246
|
+
* `--agent-starts`, so the server may hand it a standing permission's start (an agent's "Always",
|
|
247
|
+
* a schedule). Sent as a header on every device-API call — never in the capabilities body, whose
|
|
248
|
+
* strict schema on an older server would refuse the whole report.
|
|
249
|
+
*/
|
|
250
|
+
exports.LAUNCH_LEVEL = 1;
|
|
244
251
|
/**
|
|
245
252
|
* The device API, with an abort signal and a deadline. Unlike `apiRequest` it accepts the empty
|
|
246
253
|
* `204` the capabilities PUT and the report answer with.
|
|
@@ -248,7 +255,7 @@ function realLoopDeps(auth, log, signal, { forgetSession, registeredSession, reg
|
|
|
248
255
|
const deviceRequest = async (auth, method, apiPath, body, signal) => {
|
|
249
256
|
const res = await fetch(`${auth.baseUrl}${apiPath}`, {
|
|
250
257
|
method,
|
|
251
|
-
headers: { Authorization: `Bearer ${auth.token}`, "Content-Type": "application/json" },
|
|
258
|
+
headers: { Authorization: `Bearer ${auth.token}`, "Content-Type": "application/json", "X-BayChat-Launch-Level": String(exports.LAUNCH_LEVEL) },
|
|
252
259
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
253
260
|
signal: AbortSignal.any([signal, AbortSignal.timeout(WAIT_SECONDS * 1000 + REQUEST_GRACE_MS)]),
|
|
254
261
|
});
|
package/hooks/hooks.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "BayChat's denied-command card for every session, also ones started by hand: an auto-mode denial becomes a card in the session's BayChat chat once it has joined (docs/features/DENIED_COMMAND_CARD.md). No PostToolUse hook: it would start baychat on every tool call.",
|
|
2
|
+
"description": "BayChat's denied-command card for every session, also ones started by hand: an auto-mode denial becomes a card in the session's BayChat chat once it has joined (docs/features/DENIED_COMMAND_CARD.md). After a compact, a channel session is told when its BayChat feed is down (channel-check). A question left open in the terminal (a permission dialog, an MCP form) becomes one line on the owner's phone (notify-hook). No PostToolUse hook: it would start baychat on every tool call.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PermissionDenied": [
|
|
5
5
|
{
|
|
@@ -13,6 +13,30 @@
|
|
|
13
13
|
}
|
|
14
14
|
]
|
|
15
15
|
}
|
|
16
|
+
],
|
|
17
|
+
"SessionStart": [
|
|
18
|
+
{
|
|
19
|
+
"matcher": "compact",
|
|
20
|
+
"hooks": [
|
|
21
|
+
{
|
|
22
|
+
"type": "command",
|
|
23
|
+
"command": "baychat channel-check",
|
|
24
|
+
"timeout": 10
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"Notification": [
|
|
30
|
+
{
|
|
31
|
+
"matcher": "permission_prompt|elicitation_dialog|elicitation_url_dialog",
|
|
32
|
+
"hooks": [
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "baychat notify-hook",
|
|
36
|
+
"timeout": 30
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
16
40
|
]
|
|
17
41
|
}
|
|
18
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "baychat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "BayChat connector CLI — pair an agent session (Claude Code, Codex, Cursor) with BayChat, or have the relay run DeepSeek or the Cursor CLI for you",
|
|
5
5
|
"bin": {
|
|
6
6
|
"baychat": "dist/index.js"
|