Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Unreleased

- Support the CLI 0.17.1 automation stop-condition flow: the automation host
answers the new `automation/pause` reverse RPC, scheduled runs keep the
`CronPauseSelf` tool available (pausing their own schedule parks it as
`paused` until a schedule change resumes it) while still denying schedule
management, and the tool joins the Cron denylist when the automations
setting is off. On CLI 0.17.1 the tool was previously dead in both modes.
- Answer the CLI 0.17.1 `workspace/mcp/trust` reverse request with the
contract's schema-valid "trust store unavailable" result. Verified against
the installed 0.17.1 bundle that the child never sends it and that the MCP
servers the bridge injects are exempt from the native workspace trust gate.
- Classify the observed transient Z.ai account failures as typed internal
provider errors so bb's Provider retry plugin can retry the failed turn up to
four times with backoff. Retry remains owned by bb; other authentication and
Expand Down
20 changes: 20 additions & 0 deletions docs/capability-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ every native tool works against a production model. A coverage percentage would
be misleading: native CLI behavior, host services, and desktop UI features have
different integration requirements.

## Re-verification: CLI 0.17.1 drift (2026-11-02)

The installed app updated to Desktop 3.17.1 / CLI 0.17.1 (upstream sync
`a60ba6a`, 2026-10-30). The wire schemas for `session/create`/`resume`,
runtime preferences and the v4 commands are unchanged, `npm run test:live`
passed against the installed 0.17.1 (`RUNTIME_VERIFIED`, exit 0), and seven
protocol methods were added upstream; nothing the adapter uses was removed.

| 0.17.1 addition | Status |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `automation/pause` + `CronPauseSelf` tool | **Implemented.** The automation host answers the sixth `automation/*` reverse RPC (`{automationId}` → `{paused}`); scheduled runs keep `CronPauseSelf` available (their only sanctioned cron tool) while still denying schedule management, and it joins the Cron denylist when the automations setting is off. On 0.17.1 the tool was previously dead in both modes |
| `session/lifecycle/{changed,getState,ensureReady,stop,restart}` | Not used. Different failure axis from the shipped crash recovery: PR #6 restarts the bb worker after child-process death, while lifecycle/restart recovers a faulted session inside a live child. Kept as a capability-gated follow-up; 0.16.9 (the oldest supported CLI) lacks the methods |
| `workspace/mcp/trust` | **Verified and closed.** The 0.17.1 child never sends this reverse request: trust records live in the CLI's own user-domain store and rich clients record decisions through the CLI server, so the headless child has no prompt path. Servers the bridge injects at `session/create` are exempt from the workspace trust gate; servers declared in workspace config files are gated by the child itself (status `untrusted`, tools not loaded). The bridge answers the contract's schema-valid `{accepted: false, reasonCode: "workspace_mcp_trust_unavailable"}`, so a newer CLI that starts persisting decisions through the host degrades gracefully instead of hitting a capability error |
| `usage/stats`, `session/usage` deprecated upstream | Informational: hosts moved to `v4/conversation/usage`. `usage/stats` is unused; `session/usage` is still requested on the adapter's optional best-effort resume path (usage restore, `src/bridge.ts`) and remains served in 0.17.1, so the deprecation changes nothing yet |

New internal CLI modules in 0.17.1 (provider-builtin-tool-compat for GLM
server-side tools, mcp-node-fallback, off-peak retry, dynamic-workflow quota
resume, the ZCode Sites command endpoint) need no bridge work. The baseline
matrix below and the 2026-10-20 gap table remain current for everything else.

## Re-verification: unchanged upstream surface and remaining gaps (2026-10-20)

The upstream protocol surface was re-diffed against the v0.5.0 refresh pin
Expand Down
7 changes: 5 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,18 @@ as an argv array; there is no shell interpolation.

Enable `automationsEnabled` to let ZCode sessions create scheduled tasks
through the native Cron tools (`CronCreate`, `CronList`, `CronUpdate`,
`CronDelete`). The plugin answers the native `automation/*` host ports from
`CronDelete`, plus `CronPauseSelf` on CLI 0.17.1+). The plugin answers the
native `automation/*` host ports from
a local store (`automations.json` in the plugin's bridge-data directory) and
runs schedules itself:

- Runs dispatch into the session that created the task: the plugin
cold-resumes the saved native session, applies the saved model and mode,
and sends the prompt with the native automation marker, so ZCode blocks
recursive task creation for that turn. The reply streams into the same bb
thread.
thread. Runs cannot manage schedules, but may pause their own schedule
with `CronPauseSelf` when the prompt's stop condition is met; a paused
schedule only resumes through a schedule change (`CronUpdate`).
- The scheduler lives in the bridge worker and only runs while bb is open -
like ZCode Desktop, schedules do not fire while the app is closed. Runs
missed while closed are skipped; recurring schedules continue from the
Expand Down
29 changes: 28 additions & 1 deletion knowledge/contracts/protocol-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ verified:
at: 2026-10-20T11:23:47Z
- by: Codex
at: 2026-10-22T11:22:00Z
- by: zcode-agent/GLM-5.3
at: 2026-11-02T09:50:00Z
---

# Protocol contracts
Expand All @@ -47,7 +49,7 @@ the sources when `stale_after` passes or an upstream release lands.
| Boundary | Contract | Verified baseline | Upstream source |
| ------------------- | ------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| bb ↔ plugin | Provider bridge protocol 2, delta grammar 3 (SDK) | SDK `@get-bb/plugin-sdk` 0.6.49 | [bb @ 9937c46](https://github.com/get-bb/bb/tree/9937c46f5c7e369de3563eb864c8cbc4c3b9c231) |
| plugin ↔ ZCode CLI | "ZCode Protocol" version 1 over stdio JSON lines | CLI 0.17.0 (app 3.16.2), macOS; 0.16.9 in the v0.5.0 record | [ZCode @ 0a1fc56](https://github.com/zai-org/ZCode/tree/0a1fc56d279fd1001f78387bd81aec1c26767411) |
| plugin ↔ ZCode CLI | "ZCode Protocol" version 1 over stdio JSON lines | CLI 0.17.1 (app 3.17.1), macOS; 0.16.9 in the v0.5.0 record | [ZCode @ 0a1fc56](https://github.com/zai-org/ZCode/tree/0a1fc56d279fd1001f78387bd81aec1c26767411) |
| marketplace catalog | bb marketplace schema v2 (generated) | `marketplace.json` at repo HEAD | [docs/marketplace.md](../../docs/marketplace.md) |

Engine ranges (`package.json`): Node `>=22` (24+ recommended for use), bb
Expand Down Expand Up @@ -93,6 +95,31 @@ snapshots.
protocol method and v4 command surfaces are unchanged versus the 0a1fc56
pin (110 methods, none added or removed), so the
[capability audit](../../docs/capability-audit.md) inventory is current.
- **Re-verification 2026-11-02**: the installed app updated to 3.17.1 with CLI
bundle **0.17.1** (upstream sync `a60ba6a`, 2026-10-30; read from
`/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs`). `npm run
test:live` reported `RUNTIME_VERIFIED`, exit 0; the create/resume,
runtime-preferences and v4 command schemas are unchanged. Seven methods
were added upstream: `automation/pause` — now served by
`src/automations.ts`; it backs the `CronPauseSelf` stop-condition tool a
scheduled run calls on itself (params `{automationId, workspace?}` → result
`{paused}`; `paused: false` means already paused/completed/failed/unknown),
and the dispatch denylist splits into management tools versus the self-pause
exit — `CronPauseSelf` joins `CRON_TOOL_NAMES` (hidden when the setting is
off) but not the per-run denylist. `session/lifecycle/{changed,
getState,ensureReady,stop,restart}` — unused for now; they recover a faulted
session inside a live child (a different axis than the worker-restart crash
recovery) and 0.16.9 lacks them, so adoption waits on a CLI-floor decision.
`workspace/mcp/trust` — verified the 0.17.1 child never sends it to a stdio
host (no sender in the bundle; trust records live in the CLI's user-domain
store and the session-create-injected `mcpServers` are exempt from the
workspace trust gate), so the bridge answers the contract's schema-valid
`{accepted: false, reasonCode: "workspace_mcp_trust_unavailable"}`. Also:
`usage/stats` and `session/usage` are deprecated upstream (hosts moved to
`v4/conversation/usage`); `usage/stats` is unused, while `session/usage`
is still requested on the optional best-effort resume path (usage
restore in `src/bridge.ts`, failure-tolerant) and remains served in
0.17.1.

## Translation rules

Expand Down
9 changes: 7 additions & 2 deletions knowledge/decisions/feature-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,19 @@ Conclusion (2026-10-20): bb's provider bridge has **no host-service channel**
## Stage 6 — automations (v0.10.0) and Off-Peak (v0.11.0), both shipped plugin-side

Native automations shipped plugin-side (2026-10-23): `src/automations.ts`
answers the five `automation/*` reverse RPCs from a JSON store in
answers the `automation/*` reverse RPCs from a JSON store in
`BB_BRIDGE_DIR` with upstream-matching snapshots, and a bridge-worker
scheduler dispatches due tasks by cold-resuming the bound session with the
saved provider options, model and mode plus the native `automationId`
marker. The `automationsEnabled` setting (off by default) gates both the
ports and the Cron tools (denylisted when off). Limits: runs fire only
while bb is open; busy sessions defer ten minutes; three consecutive
failures park a schedule as `failed`. The upstream reference
failures park a schedule as `failed`. On 2026-11-02 the sixth method,
`automation/pause` (CLI 0.17.1), made the `CronPauseSelf` stop-condition
tool live: a run pausing its own schedule parks it as `paused` (only a
schedule-semantics update resumes it), the per-run denylist denies
schedule management but keeps the self-pause exit, and `CronPauseSelf`
joins the setting-off denylist. The upstream reference
implementations in `packages/services` (`AutomationService`/`OffPeakTaskService`,
`zcodeAgentService` handlers) informed the shapes; see the
[host-boundary amendment](zcode-desktop-host-boundary.md). Off-Peak shipped
Expand Down
9 changes: 9 additions & 0 deletions knowledge/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,12 @@
green) after removing the Experimental label (v0.14.0/v0.14.1);
procedure + the expanded-row graph rendering constraint recorded in
runbooks/marketplace-submission.md.
- 2026-11-02: closed the two CLI 0.17.1 gaps from the drift analysis —
`automation/pause` host support makes the `CronPauseSelf` stop-condition
tool live (dispatch denylist keeps the self-pause exit while still denying
schedule management; the tool joins the setting-off denylist), and
`workspace/mcp/trust` was verified never-sent by the 0.17.1 child
(bridge-injected MCP servers are exempt from the native workspace trust
gate) so the bridge answers the contract's unavailable result; the audit
gained the 2026-11-02 re-verification section and
contracts/protocol-contracts.md the 0.17.1 drift record.
53 changes: 52 additions & 1 deletion src/automations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,32 @@ import { ProtocolError } from "./client.js";
import { modelRefSchema } from "./protocol.js";

// Native automation contract mirrored from @zcode/shared on upstream main
// (pinned 2026-10-23). The CLI validates every result with its own strict
// (pinned 2026-10-23; automation/pause verified against the installed CLI
// 0.17.1 on 2026-11-02). The CLI validates every result with its own strict
// schemas, so field names, enums and the exact field set must match.
export const AUTOMATION_METHODS = new Set([
"automation/create",
"automation/update",
"automation/list",
"automation/delete",
"automation/checkTaskBinding",
"automation/pause",
]);

export const CRON_TOOL_NAMES = [
"CronCreate",
"CronList",
"CronUpdate",
"CronDelete",
"CronPauseSelf",
] as const;

// Scheduled runs must not manage schedules, but CronPauseSelf is the one
// sanctioned cron tool inside a run: it can only pause the automation that
// triggered it, so the dispatch denylist keeps it available (CLI 0.17.1+).
export const CRON_MANAGEMENT_TOOL_NAMES: readonly string[] =
CRON_TOOL_NAMES.filter((name) => name !== "CronPauseSelf");

// Upstream keeps at most 20 definitions per index; the CLI recognizes the
// limit through this token in the message and stops its retry loops.
const CREATE_LIMIT = 20;
Expand Down Expand Up @@ -529,6 +538,40 @@ export function createAutomationService(options?: {
return { automation: snapshot(next) };
}

// automation/pause is the host side of the CronPauseSelf tool a scheduled
// run calls on its own schedule when its stop condition is met. Result is
// the strict {paused} shape; paused=false means "not active, nothing
// changed" for unknown, finished or already-paused schedules.
async function pause(
raw: unknown,
cwd: string,
): Promise<{ paused: boolean }> {
const input = z
.object({
automationId: z.string().min(1),
// The native params carry the workspace object; scoping stays with
// the session cwd like every other automation method.
workspace: z.unknown().optional(),
})
.strict()
.parse(raw);
await load();
const current = loaded!.find(
(a) => a.automationId === input.automationId && a.workspacePath === cwd,
);
if (!current || current.lifecycleStatus !== "active")
return { paused: false };
// Only the lifecycle flips: due() skips paused entries, the nextRunAt is
// left for display, and a schedule-semantics update reactivates it.
loaded = loaded!.map((a) =>
a.automationId === current.automationId
? { ...a, lifecycleStatus: "paused" as const }
: a,
);
await persist(loaded);
return { paused: true };
}

async function remove(
raw: unknown,
cwd: string,
Expand Down Expand Up @@ -694,6 +737,14 @@ export function createAutomationService(options?: {
);
return remove(raw, context.cwd);
}
if (method === "automation/pause") {
if (!context.cwd)
throw new ProtocolError(
"No active bb session for this request.",
-32601,
);
return pause(raw, context.cwd);
}
if (method === "automation/checkTaskBinding") {
if (!context.cwd)
throw new ProtocolError(
Expand Down
18 changes: 17 additions & 1 deletion src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
import { History, BackgroundWork, managementCommands } from "./lifecycle.js";
import {
AUTOMATION_METHODS,
CRON_MANAGEMENT_TOOL_NAMES,
CRON_TOOL_NAMES,
createAutomationService,
dispatchPermissionMode,
Expand Down Expand Up @@ -961,6 +962,19 @@ export function createBridge(
providerOptions: session?.providerOptions,
});
}
// CLI 0.17.1 added a workspace MCP trust control plane: the native side
// keeps its own per-workspace trust store and gates servers declared in
// workspace config files behind it; the mcpServers this bridge injects at
// session/create are exempt. Verified against the installed 0.17.1 bundle
// (2026-11-02): the child never sends this reverse request - trust
// decisions are recorded client-side - but the contract defines an
// explicit "trust store unavailable" answer, which stays schema-valid and
// retry-free if a newer CLI starts persisting decisions through the host.
if (method === "workspace/mcp/trust")
return {
accepted: false,
reasonCode: "workspace_mcp_trust_unavailable",
};
// These native ports need a host service bb's provider bridge does not
// expose (verified against SDK 0.6.49: BRIDGE_REQUEST_METHODS has no
// host-service channel). Reject uniformly with an actionable reason
Expand Down Expand Up @@ -1327,14 +1341,16 @@ export function createBridge(
if (!session) {
// Cold-resume the bound native session with the settings snapshot
// captured at creation; scheduled turns never manage schedules.
// CronPauseSelf stays available: pausing its own schedule when the
// run's stop condition is met is the sanctioned exit (CLI 0.17.1+).
session = await construct(
{
threadId: entry.threadId,
cwd: entry.workspacePath,
providerThreadId: entry.targetTaskId,
instructionMode: "replace",
options: automationOptions(entry),
disallowedTools: [...CRON_TOOL_NAMES],
disallowedTools: [...CRON_MANAGEMENT_TOOL_NAMES],
},
true,
);
Expand Down
Loading
Loading