docks-kit 0.8.2 → 0.9.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/AGENTS.md CHANGED
@@ -36,6 +36,7 @@ Codex SoT notes:
36
36
  - `SoT/.codex/rules/*.rules` deploys to `~/.codex/rules/` as kit-managed Codex command policy. This is Codex's equivalent of permission allow/prompt/block rules; user-learned approvals in `~/.codex/rules/default.rules` are preserved.
37
37
  - `SoT/.codex/plugins/marketplace.json` deploys to Codex's personal marketplace path at `~/.agents/plugins/marketplace.json`; when the `codex` CLI is available, sync reruns `codex plugin add <plugin@marketplace>` for enabled SoT plugins so stale cached installs are refreshed.
38
38
  - `docks-kit status` verifies Session Relay only through the supported `codex plugin list --json` inventory. `ready` means installed and enabled for a newly started Codex session; it is not evidence about an old process, lifecycle state, receive-path health, or worker quiescence. The global prompt SoTs carry the owner's standing authorization for Docks cross-company plan review, which never overrides host or platform denial.
39
+ - Claude and Codex sync call `sessionRelayCli.ts ensureSessionRelayCli` immediately before their plugin passes. It installs the exact source-pinned precompiled command at `~/.local/bin/session-relay` for Linux/macOS x64/arm64 only, requires committed digest = same-release `SHA256SUMS` row = downloaded bytes, smoke-tests the staged version, and atomically replaces the stable path. `agents`-only sync never enters this boundary. The committed digests remain fixture-only until the four `session-relay--v0.12.0` production assets exist; do not publish them as release evidence.
39
40
  - Both global prompt SoTs carry one byte-identical compact `Docks-workflow-models:` record. Root `--model-orchestrator` / `--model-reviewer` / `--model-implementer` / review-bound flags update only that deployed record; `docks-kit models workflow` lists the closed selectors, and a flag-less sync restores defaults.
40
41
  - Codex workflow selectors use the closed `<tool>:<model>@<effort>[+fast]` routing grammar. Fast is intentionally absent from the global SoT: no suffix means Standard and keeps the backward-compatible schema-1 record; `+fast` emits a schema-2 candidate with `service_tier: "fast"`. Docks and Session Relay consumers must explicitly launch unsuffixed Codex roles with the default service tier so a user's global Fast preference cannot leak into workflows.
41
42
  - The `codex` CLI binary is upstream-owned, not kit-owned. The official standalone installer keeps package metadata under `$CODEX_HOME/packages/standalone` and places the `codex` symlink in `~/.local/bin` by default; sync only warns with a download-then-run installer command when the CLI is missing. Existing installs can self-update with `codex update`; npm and Homebrew remain upstream alternatives.
@@ -52,7 +53,7 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`), see the matching SoT d
52
53
  - **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, and `agents`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync codex` for Codex-only config edits); targets can be combined with `--dry-run`, `--skip-rtk`, `--skip-plugin-refresh` (install missing plugins without refreshing existing caches; used by `docks-kit update`), `--reconcile`, `--prune`, `--yes` (auto-accept toolchain prompts), and the deploy-time modifiers `--claude-compact-window=<tokens>` / `--claude-permissive` / `--claude-model=<m>` / `--claude-effort=<level>` / `--claude-advisor=<on|off|default>` / `--codex-model=<m>` / `--codex-effort=<level>` (see `CLAUDE.md` § Deploy-time modifiers).
53
54
  - **Additive by default.** Keys present in deployed config but absent from SoT are preserved on default sync. This protects user-only additions, but means drift accumulates — neither flag-less reset can clean it up. The one exception is the Claude `removed` manifest (`claude::_removed_manifest`), a curated list of unambiguous kit-owned artifacts that `claude::sync_removals` force-prunes on every sync; see `CLAUDE.md` § Pruning stale artifacts.
54
55
  - **`--reconcile` / `--prune` are the kit-owned reconcile flags.** Orthogonal — `--reconcile` reconciles the settings layer (SoT-declared keys/tables/arrays win; user-only keys and nested objects are preserved; permissions arrays are replaced wholesale by SoT). `--prune` uninstalls kit-managed installations not in the SoT (plugins, marketplaces, and `~/.agents/skills/*` entries tracked in `~/.agents/.kit-managed-skills`). Combine for a full reset to SoT's kit-managed scope. User-only additions outside the kit's scope (custom env vars, mcpServers, manually-installed skills, third-party plugins not declared in SoT) are always preserved. Each tool's per-tool file documents the specific paths and diff recipes.
55
- - **SOLID-aligned modules.** `cli/src/engine-native/parseArgs.ts` owns flag parsing/validation. `toolchain.ts` owns the verified-version gate over `SoT/toolchain.json`; `bun.ts` owns the shared, memoized Bun bootstrap; `claudeRuntime.ts` owns Claude settings materialization. `claudeSync.ts`, `codexSync.ts`, and `skillsSync.ts` own tool-specific sync logic. `index.ts` is the thin orchestrator. The public CLI seam is `cli/src/engine.ts`.
56
+ - **SOLID-aligned modules.** `cli/src/engine-native/parseArgs.ts` owns flag parsing/validation. `toolchain.ts` owns the verified-version gate over `SoT/toolchain.json`; `sessionRelayCli.ts` owns the closed release manifest, platform mapping, checksum/smoke transaction, and atomic Session Relay replacement; `bun.ts` owns the shared, memoized Bun bootstrap; `claudeRuntime.ts` owns Claude settings materialization. `claudeSync.ts`, `codexSync.ts`, and `skillsSync.ts` own tool-specific sync logic. `index.ts` is the thin orchestrator. The public CLI seam is `cli/src/engine.ts`.
56
57
  - **Small, reviewable changes.** Bundled multi-concern PRs are harder to review and revert. Split an engine/CLI change and a per-tool config change unless the change requires atomicity.
57
58
  - **Dry-run before destructive flags.** Always preview with `./docks-kit sync --dry-run` (or the relevant `diff <(jq -S …)` recipe in the per-tool file) before invoking `--reconcile` or `--prune`. User-added permissions / env vars / plugins absent from SoT will be discarded.
58
59
  - **SoT prompt files are rules, not explanation.** `SoT/.claude/CLAUDE.md` and `SoT/.codex/AGENTS.md` are loaded into every agent session's prompt context — every line costs prompt tokens on every turn for every user. Restrict their content to rules, heuristics, and `<constraint>` blocks the agent must *act on* during a turn. Do NOT add inline source citations (`Source: …`, attributed quotes), "why this rule exists" preface text, version-watermarking trivia (e.g. "Distilled from X v2.0, captured 2025-11-07"), per-bug workarounds, or installation instructions. Provenance, motivation, and historical context belong in `CLAUDE.md` / `AGENTS.md` at the repo root (humans read once) or in commit messages — never in the SoT. For every line, apply the official test: would removing it cause the agent to make mistakes? If not, cut it — over-instruction degrades adherence on current frontier models.
@@ -91,7 +92,7 @@ When a kit-mechanic skill, its `references/`, or a wrapper agent (`.claude/agent
91
92
  Multi-commit work plans live in `docs/plans/active/` (status is a frontmatter field) and `docs/plans/finished/` (archive). Every plan file is a complete cold-handoff document — goal, context & rationale, environment & how-to-run, steps with exact paths, executable acceptance criteria, and a binary cold-handoff checklist — so any agent (or a weaker model) can pick one up cold without guessing. Skills handle every operation: `plan-init` (bootstrap/migrate), `plan-manager` (list/show/start/block/ship/new, auto-commit on transition, self-review on draft), `plan-review` (verification). Trigger by natural language or the matching `plan-*` skill. `active/` is multi-occupancy.
92
93
  </constraint>
93
94
 
94
- The full convention (frontmatter schema, body sections, one-pass local self-review, bounded strong-default X/S review receipts, workflow roles, open-questions, age tokens) lives in `docs/plans/AGENTS.md`. `docs/plans/CLAUDE.md` is a one-line `@AGENTS.md` import for Claude Code's nested discovery. If `.codex/agents/plan-manager.toml` and `.codex/agents/plan-review.toml` exist, Codex may use them for explicit subagent delegation; otherwise run the matching `plan-*` skill inline.
95
+ The full convention (frontmatter schema, body sections, one-pass local checklist self-review, schema-5 role `primary`, GPT-5.6-sol/high/`service_tier:"default"` (Standard) → Fable/high Opus/xhigh availability-only fallback, exact eight-criterion evidence checklist, primary-role waivers, one full round plus at most one accepted-blocker repair, open questions, and age tokens) lives in `docs/plans/AGENTS.md`. `docs/plans/CLAUDE.md` is a one-line `@AGENTS.md` import for Claude Code's nested discovery. If `.codex/agents/plan-manager.toml` and `.codex/agents/plan-review.toml` exist, Codex may use them for explicit subagent delegation; otherwise run the matching `plan-*` skill inline.
95
96
 
96
97
  Distinct from per-tool **Open Concerns** sections (wait-on-upstream
97
98
  blockers tied to a vendor shipping a fix — these live inside the per-tool
package/README.md CHANGED
@@ -102,6 +102,12 @@ current valid values, while a later flag-less sync restores all defaults.
102
102
  versions above the kit-verified pin prompt before installing
103
103
  (`--yes` accepts; non-TTY falls back to the pinned verified version
104
104
  when possible). `docks-kit toolchain check` shows the full table.
105
+ - **Session Relay CLI** — Claude/Codex syncs ensure the source-pinned
106
+ precompiled `session-relay` command in `~/.local/bin` immediately before
107
+ Session Relay plugin reconciliation. Linux/macOS x64/arm64 assets must match
108
+ both the committed digest and their release `SHA256SUMS`, then pass an exact
109
+ staged version smoke before atomic replacement; failures preserve any prior
110
+ command. Production digests for `session-relay--v0.12.0` are still pending.
105
111
  - **Model catalog** — `SoT/models.json` is the research-verified source for
106
112
  model validation, listings, pickers, and the Docks workflow-role registry.
107
113
  - **Claude runtime** — sync materializes three dependency-free Bun `.mjs`
@@ -26,10 +26,15 @@ Order matters — runtime readiness and settings form one transaction:
26
26
  `--claude-model`, `--claude-effort`, `--claude-advisor`) — deployed file only.
27
27
  7. ~/.claude.json (showTurnDuration, user-scoped MCP servers) and connector env
28
28
  export.
29
- 8. **Plugins** — seven idempotent passes via the `claude plugin` CLI
29
+ 8. **Session Relay CLI** — ensure the exact source-pinned precompiled command
30
+ in `~/.local/bin` for the supported host. A staged asset must match both the
31
+ source digest and same-release `SHA256SUMS`, then report exact version before
32
+ atomic replacement. Failure stops before Session Relay plugin work and
33
+ preserves any prior command.
34
+ 9. **Plugins** — seven idempotent passes via the `claude plugin` CLI
30
35
  (marketplaces → install → update → [--prune: uninstall/remove] → re-assert
31
36
  SoT enabled-state). Optional opt-ins via `--claude-plugin=<name>`.
32
- 9. LSP server binaries (npm globals).
37
+ 10. LSP server binaries (npm globals).
33
38
 
34
39
  The statusline reads Claude's native `rate_limits`. There is no OAuth request,
35
40
  usage cache, jq/curl runtime dependency, or Stop fetch hook.
@@ -40,12 +45,15 @@ bubblewrap check (Linux), config.toml merge (top-level keys replaced
40
45
  per-key, [table] blocks replaced wholesale, user-only keys/tables preserved),
41
46
  `--codex-model` then `--codex-effort` modifiers, rules, AGENTS.md, personal
42
47
  marketplace file, `codex plugin add` refresh.
48
+ Immediately before `codex plugin add`, the same Session Relay CLI ensure runs;
49
+ an ensure failure prevents the plugin pass.
43
50
 
44
51
  ## agents (→ ~/.agents/skills, ~/.claude/skills symlinks)
45
52
 
46
53
  `npx skills add` per missing manifest slug, Claude symlink healing,
47
54
  CLI binaries (agent-browser, effect-solutions — toolchain-gated), and the
48
55
  kit-managed snapshot that `--prune` reconciles against.
56
+ This target never ensures or downloads Session Relay.
49
57
 
50
58
  ## Reconcile flags
51
59
 
@@ -4,8 +4,8 @@
4
4
 
5
5
  | Field | Meaning |
6
6
  |-------|---------|
7
- | `kind` | `check` (doctor visibility) / `managed` (kit installs + upgrades) / `pin` (no binary — a version pin for npx-invoked tools, e.g. `skills-cli`) |
8
- | `policy` | `track` (upgrade toward latest, gated by `verified`) / `present` (install when missing, never upgrade) |
7
+ | `kind` | `check` (doctor visibility) / `managed` (kit installs + upgrades) / `managed-release` (dedicated source-pinned release transaction) / `pin` (no binary — a version pin for npx-invoked tools, e.g. `skills-cli`) |
8
+ | `policy` | `track` (upgrade toward latest, gated by `verified`) / `present` (install when missing, never upgrade) / `exact` (install the declared release only) |
9
9
  | `floor` | Minimum acceptable version (below → upgrade automatically) |
10
10
  | `verified` | Last kit-tested version — the gate line |
11
11
  | `pinnable` | Whether an exact version can be installed (rtk: `RTK_VERSION=vX.Y.Z`) |
@@ -36,6 +36,17 @@ now kit-approved" act.
36
36
  toolchain ensure. Windows resolves only a real absolute `bun.exe` for hooks.
37
37
  - **effect-solutions**, **agent-browser** — policy `track`: self-upgrade
38
38
  toward npm latest, gated by their `verified` pins.
39
+ - **session-relay** — policy `exact`: Claude/Codex sync and
40
+ `docks-kit toolchain ensure session-relay` select exactly one of four targets
41
+ (`x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl`,
42
+ `x86_64-apple-darwin`, `aarch64-apple-darwin`). The installer downloads the
43
+ pinned `session-relay--v0.12.0` asset and same-release `SHA256SUMS`, requires
44
+ source pin = selected row = downloaded bytes, chmods/smoke-tests only the
45
+ sibling stage, then renames it over `~/.local/bin/session-relay`. Offline,
46
+ unsupported-host, checksum, chmod, version, download, or rename failures
47
+ preserve an existing command byte-for-byte. The four committed digests are
48
+ validation fixtures pending independently hashed production assets; this
49
+ pin must not ship until they are replaced and reviewed.
39
50
 
40
51
  jq and curl are `check` rows, not global prerequisites. jq is not consumed by
41
52
  normal sync. curl is checked only at a requested POSIX RTK/Bun installer
@@ -55,5 +66,6 @@ probe falls back to the pinned `verified`, never to an ungated latest.
55
66
  ```
56
67
  docks-kit toolchain check # doctor table (also inside docks-kit status)
57
68
  docks-kit toolchain ensure rtk # install/upgrade one tool per policy
69
+ docks-kit toolchain ensure session-relay # install exact verified release
58
70
  docks-kit sync --yes # unattended: auto-accept gates
59
71
  ```
@@ -32,6 +32,7 @@ import { mergeSettings, reconcileSettings } from "./settings"
32
32
  import { ensure, field } from "./toolchain"
33
33
  import { payloadBytes, payloadDisplayPath, payloadText } from "../payload"
34
34
  import { renderDefaultWorkflowInstructions } from "./workflowDeploy"
35
+ import { ensureSessionRelayCli } from "./sessionRelayCli"
35
36
 
36
37
  export type ClaudeRuntimeState =
37
38
  | { readonly kind: "ready"; readonly paths: ClaudeRuntimePaths }
@@ -82,6 +83,7 @@ export function claudeSync(ctx: Ctx): ClaudeRuntimeState {
82
83
  syncClaudeAdvisor(ctx, ctx.claudeAdvisor)
83
84
  syncClaudeJson(ctx)
84
85
  syncConnectorEnv(ctx)
86
+ ensureSessionRelayCli(ctx)
85
87
  syncPlugins(ctx, claudeDir)
86
88
  syncOptionalPlugins(ctx, claudeDir)
87
89
  syncLspServers(ctx)
@@ -13,6 +13,7 @@ import { compareCodepoints, isObject, jqStringify, parseJson, type Json } from "
13
13
  import { sessionRelayReadiness } from "./sessionRelayReadiness"
14
14
  import { payloadBytes, payloadDisplayPath, payloadPaths, payloadText, type PayloadPath } from "../payload"
15
15
  import { renderDefaultWorkflowInstructions } from "./workflowDeploy"
16
+ import { ensureSessionRelayCli } from "./sessionRelayCli"
16
17
 
17
18
  export function codexSync(ctx: Ctx): void {
18
19
  const codexDir = p(ctx.home, ".codex")
@@ -28,6 +29,7 @@ export function codexSync(ctx: Ctx): void {
28
29
  syncAgentsMd(ctx, renderDefaultWorkflowInstructions(payloadText("SoT/.codex/AGENTS.md")), p(codexDir, "AGENTS.md"))
29
30
  syncMarketplace(ctx, payloadText("SoT/.codex/plugins/marketplace.json"), p(ctx.agentsDir, "plugins", "marketplace.json"))
30
31
  removeLegacyDocksMarketplace(ctx, userConfig)
32
+ ensureSessionRelayCli(ctx)
31
33
  syncPlugins(ctx, sotConfig)
32
34
  }
33
35
 
@@ -29,6 +29,7 @@ export type ToolId =
29
29
  | "bwrap"
30
30
  | "agent-browser"
31
31
  | "effect-solutions"
32
+ | "session-relay"
32
33
  | "chrome-for-testing"
33
34
  | "ffplay"
34
35
  | "intelephense"
@@ -260,6 +261,11 @@ export const DEPENDENCIES: Record<ToolId, DependencySpec> = {
260
261
  version: versionProbe("rtk"),
261
262
  latest: latestRtk
262
263
  }),
264
+ "session-relay": spec("session-relay", "optional", () => "docks-kit toolchain ensure session-relay", {
265
+ resolve: (exec) => pathProbe(p(home(), ".local", "bin", "session-relay"))(exec),
266
+ version: (exec) => exec.capture(p(home(), ".local", "bin", "session-relay"), ["--version"]),
267
+ locate: () => ({ path: p(home(), ".local", "bin", "session-relay"), binDir: p(home(), ".local", "bin") })
268
+ }),
263
269
  bun: spec(
264
270
  "bun",
265
271
  "optional",
@@ -12,6 +12,7 @@ import type { Ctx } from "./index"
12
12
  import { isObject, parseJson, type Json } from "./jq"
13
13
  import { printModels, validateClaudeModel, validateCodexModel } from "./models"
14
14
  import { ensureRtk } from "./claudeSync"
15
+ import { ensureSessionRelayCli } from "./sessionRelayCli"
15
16
  import { bunBootstrap } from "./bun"
16
17
  import { agentBrowserInstall, effectSolutionsInstall } from "./skillsSync"
17
18
  import { ensure, report } from "./toolchain"
@@ -142,8 +143,10 @@ export function modeToolchain(ctx: Ctx, args: ReadonlyArray<string>): number {
142
143
  return ensure(ctx, "effect-solutions", effectSolutionsInstall(ctx))
143
144
  case "agent-browser":
144
145
  return ensure(ctx, "agent-browser", agentBrowserInstall)
146
+ case "session-relay":
147
+ return ensureSessionRelayCli(ctx)
145
148
  default:
146
- err("toolchain ensure supports managed tools only (rtk, bun, effect-solutions, agent-browser)")
149
+ err("toolchain ensure supports managed tools only (rtk, bun, effect-solutions, agent-browser, session-relay)")
147
150
  return 2
148
151
  }
149
152
  }
@@ -0,0 +1,257 @@
1
+ import { spawnSync } from "node:child_process"
2
+ import { createHash, randomBytes } from "node:crypto"
3
+ import {
4
+ chmodSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ renameSync,
9
+ rmSync,
10
+ statSync
11
+ } from "node:fs"
12
+ import { basename, dirname, join } from "node:path"
13
+
14
+ import { isObject, parseJson, type Json } from "./jq"
15
+ import type { Ctx } from "./index"
16
+ import { ExitError } from "./parseArgs"
17
+ import { payloadText } from "../payload"
18
+
19
+ const VERSION = "0.12.0"
20
+ const REPOSITORY = "DocksDocks/docks"
21
+ const TAG = "session-relay--v0.12.0"
22
+ const PLUGIN_ID = "session-relay@docks"
23
+ const INSTALL_PATH = "~/.local/bin/session-relay"
24
+ const TARGETS = [
25
+ "x86_64-unknown-linux-musl",
26
+ "aarch64-unknown-linux-musl",
27
+ "x86_64-apple-darwin",
28
+ "aarch64-apple-darwin"
29
+ ] as const
30
+
31
+ export type SessionRelayTarget = typeof TARGETS[number]
32
+
33
+ export interface SessionRelayManifest {
34
+ readonly kind: "managed-release"
35
+ readonly policy: "exact"
36
+ readonly verified: typeof VERSION
37
+ readonly repository: typeof REPOSITORY
38
+ readonly tag: typeof TAG
39
+ readonly plugin_id: typeof PLUGIN_ID
40
+ readonly plugin_version: typeof VERSION
41
+ readonly install_path: typeof INSTALL_PATH
42
+ readonly assets: Readonly<Record<SessionRelayTarget, string>>
43
+ }
44
+
45
+ export interface SessionRelayInstallOps {
46
+ readonly download: (url: string, destination: string) => boolean
47
+ readonly chmod: (path: string, mode: number) => void
48
+ readonly runVersion: (path: string) => { readonly ok: boolean; readonly stdout: string }
49
+ readonly rename: (from: string, to: string) => void
50
+ readonly uniqueSuffix: () => string
51
+ }
52
+
53
+ export interface SessionRelayInstallInput {
54
+ readonly home: string
55
+ readonly dryRun: boolean
56
+ readonly platform: string
57
+ readonly arch: string
58
+ readonly manifestText: string
59
+ readonly log: (line: string) => void
60
+ readonly error?: (line: string) => void
61
+ }
62
+
63
+ function fail(input: SessionRelayInstallInput, message: string): never {
64
+ input.error?.(message)
65
+ const error = new ExitError(1)
66
+ error.message = message
67
+ throw error
68
+ }
69
+
70
+ function exactKeys(value: { [key: string]: Json }, expected: ReadonlyArray<string>, label: string): void {
71
+ const actual = Object.keys(value).sort()
72
+ const wanted = [...expected].sort()
73
+ if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
74
+ throw new Error(`${label} violates the closed Session Relay manifest schema`)
75
+ }
76
+ }
77
+
78
+ export function parseSessionRelayManifest(text: string): SessionRelayManifest {
79
+ const value = parseJson(text)
80
+ if (value === undefined || !isObject(value)) throw new Error("Session Relay manifest is not a JSON object")
81
+ exactKeys(
82
+ value,
83
+ ["kind", "policy", "verified", "repository", "tag", "plugin_id", "plugin_version", "install_path", "assets"],
84
+ "Session Relay manifest"
85
+ )
86
+ const expected = {
87
+ kind: "managed-release",
88
+ policy: "exact",
89
+ verified: VERSION,
90
+ repository: REPOSITORY,
91
+ tag: TAG,
92
+ plugin_id: PLUGIN_ID,
93
+ plugin_version: VERSION,
94
+ install_path: INSTALL_PATH
95
+ } as const
96
+ for (const [key, wanted] of Object.entries(expected)) {
97
+ if (value[key] !== wanted) throw new Error(`Session Relay manifest ${key.replaceAll("_", " ")} must be ${wanted}`)
98
+ }
99
+ const assets = value["assets"]
100
+ if (!isObject(assets)) throw new Error("Session Relay manifest assets must be an object")
101
+ exactKeys(assets, TARGETS, "Session Relay manifest assets target set")
102
+ const parsedAssets = {} as Record<SessionRelayTarget, string>
103
+ for (const target of TARGETS) {
104
+ const digest = assets[target]
105
+ if (typeof digest !== "string" || !/^[0-9a-f]{64}$/.test(digest)) {
106
+ throw new Error(`Session Relay manifest digest for ${target} must be 64 lowercase hex characters`)
107
+ }
108
+ parsedAssets[target] = digest
109
+ }
110
+ return { ...expected, assets: parsedAssets }
111
+ }
112
+
113
+ function manifestEntry(): string {
114
+ const document = parseJson(payloadText("SoT/toolchain.json"))
115
+ const tools = document !== undefined && isObject(document) ? document["tools"] : undefined
116
+ const entry = tools !== undefined && isObject(tools) ? tools["session-relay"] : undefined
117
+ if (entry === undefined) throw new Error("Embedded toolchain manifest has no session-relay entry")
118
+ return JSON.stringify(entry)
119
+ }
120
+
121
+ export function sessionRelayTarget(platform: string, arch: string): SessionRelayTarget {
122
+ if (platform === "linux" && arch === "x64") return "x86_64-unknown-linux-musl"
123
+ if (platform === "linux" && arch === "arm64") return "aarch64-unknown-linux-musl"
124
+ if (platform === "darwin" && arch === "x64") return "x86_64-apple-darwin"
125
+ if (platform === "darwin" && arch === "arm64") return "aarch64-apple-darwin"
126
+ throw new Error(`Unsupported host for Session Relay CLI: ${platform}/${arch}; supported: linux|darwin x64|arm64`)
127
+ }
128
+
129
+ function trimOneLineEnding(text: string): string {
130
+ if (text.endsWith("\r\n")) return text.slice(0, -2)
131
+ if (text.endsWith("\n")) return text.slice(0, -1)
132
+ return text
133
+ }
134
+
135
+ function exactVersion(ops: SessionRelayInstallOps, path: string): boolean {
136
+ const result = ops.runVersion(path)
137
+ return result.ok && trimOneLineEnding(result.stdout) === `session-relay ${VERSION}`
138
+ }
139
+
140
+ function selectedChecksum(text: string, assetName: string): string {
141
+ const selected = text.split("\n").filter((line) => line.endsWith(` ${assetName}`))
142
+ if (selected.length !== 1 || !new RegExp(`^[0-9a-f]{64} ${assetName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`).test(selected[0]!)) {
143
+ throw new Error(`SHA256SUMS must contain exactly one canonical row for ${assetName}`)
144
+ }
145
+ return selected[0]!.slice(0, 64)
146
+ }
147
+
148
+ const defaultOps: SessionRelayInstallOps = {
149
+ download: (url, destination) => {
150
+ const result = spawnSync("curl", ["-fL", "--retry", "2", "--connect-timeout", "10", "--output", destination, url], {
151
+ stdio: "inherit"
152
+ })
153
+ return result.error === undefined && result.status === 0
154
+ },
155
+ chmod: chmodSync,
156
+ runVersion: (path) => {
157
+ const result = spawnSync(path, ["--version"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] })
158
+ return { ok: result.error === undefined && result.status === 0, stdout: result.stdout ?? "" }
159
+ },
160
+ rename: renameSync,
161
+ uniqueSuffix: () => `${process.pid}-${randomBytes(8).toString("hex")}`
162
+ }
163
+
164
+ export function installSessionRelayCli(
165
+ input: SessionRelayInstallInput,
166
+ ops: SessionRelayInstallOps = defaultOps
167
+ ): void {
168
+ let manifest: SessionRelayManifest
169
+ let target: SessionRelayTarget
170
+ try {
171
+ manifest = parseSessionRelayManifest(input.manifestText)
172
+ target = sessionRelayTarget(input.platform, input.arch)
173
+ } catch (error) {
174
+ fail(input, error instanceof Error ? error.message : String(error))
175
+ }
176
+
177
+ if (input.dryRun) {
178
+ input.log(
179
+ `[dry-run] ensure Session Relay CLI ${manifest.verified} from ${manifest.repository}@${manifest.tag} (${target}) -> ${manifest.install_path}`
180
+ )
181
+ return
182
+ }
183
+
184
+ const stable = join(input.home, ".local", "bin", "session-relay")
185
+ if (existsSync(stable) && exactVersion(ops, stable)) return
186
+
187
+ const parent = dirname(stable)
188
+ try {
189
+ mkdirSync(parent, { recursive: true, mode: 0o755 })
190
+ if (!statSync(parent).isDirectory()) {
191
+ fail(input, `Session Relay install parent is not a directory: ${parent}`)
192
+ }
193
+ } catch (error) {
194
+ if (error instanceof ExitError) throw error
195
+ fail(input, `Cannot prepare Session Relay install directory ${parent}: ${error instanceof Error ? error.message : String(error)}`)
196
+ }
197
+
198
+ const suffix = ops.uniqueSuffix()
199
+ const stage = join(parent, `.session-relay.stage-${suffix}`)
200
+ const checksumFile = join(parent, `.session-relay.checksums-${suffix}`)
201
+ const assetName = `session-relay-${target}`
202
+ const baseUrl = `https://github.com/${manifest.repository}/releases/download/${manifest.tag}`
203
+
204
+ try {
205
+ if (!ops.download(`${baseUrl}/${assetName}`, stage)) fail(input, `Failed to download pinned Session Relay asset ${assetName}`)
206
+ if (!ops.download(`${baseUrl}/SHA256SUMS`, checksumFile)) fail(input, "Failed to download pinned Session Relay SHA256SUMS")
207
+
208
+ let checksumDigest: string
209
+ try {
210
+ checksumDigest = selectedChecksum(readFileSync(checksumFile, "utf8"), assetName)
211
+ } catch (error) {
212
+ fail(input, error instanceof Error ? error.message : String(error))
213
+ }
214
+ const sourceDigest = manifest.assets[target]
215
+ if (checksumDigest !== sourceDigest) fail(input, `Session Relay source pin does not match SHA256SUMS for ${assetName}`)
216
+ const downloadedDigest = createHash("sha256").update(readFileSync(stage)).digest("hex")
217
+ if (downloadedDigest !== sourceDigest) fail(input, `Downloaded Session Relay checksum mismatch for ${assetName}`)
218
+
219
+ try {
220
+ ops.chmod(stage, 0o755)
221
+ } catch (error) {
222
+ fail(input, `Failed to chmod staged Session Relay CLI: ${error instanceof Error ? error.message : String(error)}`)
223
+ }
224
+ if (!exactVersion(ops, stage)) fail(input, `Staged Session Relay CLI did not report exact version session-relay ${VERSION}`)
225
+ try {
226
+ ops.rename(stage, stable)
227
+ } catch (error) {
228
+ fail(input, `Failed to atomically replace Session Relay CLI: ${error instanceof Error ? error.message : String(error)}`)
229
+ }
230
+ input.log(`Session Relay CLI ready (${VERSION})`)
231
+ } finally {
232
+ try {
233
+ rmSync(stage, { force: true })
234
+ } catch {
235
+ // A cleanup failure must not turn a successful atomic replacement into
236
+ // an install failure or alter a pre-existing stable executable.
237
+ }
238
+ try {
239
+ rmSync(checksumFile, { force: true })
240
+ } catch {
241
+ // Same failure-preservation rule as the staged executable above.
242
+ }
243
+ }
244
+ }
245
+
246
+ export function ensureSessionRelayCli(ctx: Ctx): number {
247
+ installSessionRelayCli({
248
+ home: ctx.home,
249
+ dryRun: ctx.dryRun,
250
+ platform: ctx.services.platform.raw(),
251
+ arch: process.arch,
252
+ manifestText: manifestEntry(),
253
+ log: ctx.dryRun ? ctx.services.logger.echo : ctx.services.logger.change,
254
+ error: ctx.services.logger.err
255
+ })
256
+ return 0
257
+ }
@@ -57,6 +57,8 @@ export function installedVersion(ctx: Ctx, tool: ToolId): string {
57
57
  case "codex":
58
58
  case "agent-browser":
59
59
  return firstLineField(version(), -1)
60
+ case "session-relay":
61
+ return firstLineField(version(), 1)
60
62
  case "git":
61
63
  return firstLineField(version(), 2)
62
64
  case "node":
@@ -1,12 +1,12 @@
1
1
  // Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
2
2
  // Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
3
3
 
4
- export const GENERATED_PACKAGE_VERSION = "0.8.2"
4
+ export const GENERATED_PACKAGE_VERSION = "0.9.0"
5
5
 
6
6
  export const GENERATED_PAYLOAD_TEXT = {
7
7
  "SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
8
8
  "SoT/models.json": "{\n \"$comment\": \"Kit-verified model catalog — single source for EngineNative validators, the docks-kit CLI (models/model commands, workflow selectors, pickers, bare-flag help), and docs. Entries are research-proofed: update an entry and its tool-level `verified` date when a model ships or retires. Deploy-time model flags remain permissive; workflow selectors are strict.\",\n \"claude\": {\n \"verified\": \"2026-07-08\",\n \"models\": [\n { \"id\": \"best\", \"kind\": \"alias\", \"note\": \"Fable 5 where the org has access, latest Opus otherwise (Claude Code >=2.1.170)\" },\n { \"id\": \"opus\", \"kind\": \"alias\", \"note\": \"latest Opus (currently Opus 4.8)\" },\n { \"id\": \"fable\", \"kind\": \"alias\", \"note\": \"Fable 5 — the kit SoT default; needs org access + Claude Code >=2.1.170\" },\n { \"id\": \"sonnet\", \"kind\": \"alias\", \"note\": \"latest Sonnet (currently Sonnet 5)\" },\n { \"id\": \"haiku\", \"kind\": \"alias\", \"note\": \"latest Haiku (currently Haiku 4.5)\" },\n { \"id\": \"default\", \"kind\": \"alias\", \"note\": \"engine pseudo-value: deletes the deployed model key so the account default applies\" },\n { \"id\": \"claude-fable-5\", \"kind\": \"id\", \"note\": \"Fable 5\" },\n { \"id\": \"claude-opus-4-8\", \"kind\": \"id\", \"note\": \"Opus 4.8\" },\n { \"id\": \"claude-sonnet-5\", \"kind\": \"id\", \"note\": \"Sonnet 5\" },\n { \"id\": \"claude-haiku-4-5-20251001\", \"kind\": \"id\", \"note\": \"Haiku 4.5\" }\n ]\n },\n \"codex\": {\n \"verified\": \"2026-07-16\",\n \"models\": [\n { \"id\": \"gpt-5.6-sol\", \"kind\": \"id\", \"note\": \"GPT-5.6 Sol — frontier, recommended default; the kit SoT pin\" },\n { \"id\": \"gpt-5.6-terra\", \"kind\": \"id\", \"note\": \"GPT-5.6 Terra — balanced tier\" },\n { \"id\": \"gpt-5.6-luna\", \"kind\": \"id\", \"note\": \"GPT-5.6 Luna — fast/light tier\" },\n { \"id\": \"gpt-5.5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5.5\" },\n { \"id\": \"gpt-5.1\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5\" }\n ]\n },\n \"workflow\": {\n \"schema\": 2,\n \"profiles\": {\n \"claude-best\": {\n \"candidates\": [\n { \"company\": \"anthropic\", \"tool\": \"claude\", \"model\": \"fable\", \"effort\": \"high\" },\n { \"company\": \"anthropic\", \"tool\": \"claude\", \"model\": \"opus\", \"effort\": \"xhigh\" }\n ]\n }\n },\n \"defaults\": {\n \"orchestrator\": \"profile:claude-best\",\n \"reviewer\": \"codex:gpt-5.6-sol@high\",\n \"implementer\": \"codex:gpt-5.6-sol@high\",\n \"review\": {\n \"minimum_score\": 90,\n \"max_rounds\": 3\n }\n },\n \"exact_target_grammar\": \"<tool>:<model>@<effort>[+fast]\",\n \"availability\": \"checked_when_used\"\n }\n}\n",
9
- "SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest — DATA only (versions, floors, policy); check/install logic lives in cli/src/engine-native/toolchain.ts with per-surface sync callbacks in cli/src/engine-native/. kind: check (doctor visibility only) | managed (kit installs/upgrades it) | pin (no binary probe — a version pin for a tool the kit invokes via npx). policy (managed only): track (upgrade toward latest, gated by `verified`) | present (install when missing, never upgrade). `verified` = last kit-tested version — anything above it prompts before install (--yes auto-accepts; non-TTY declines and falls back to the pinned `verified` when pinnable). Supply-chain stance: every kit-driven install is pinned to `verified` or gated by it — never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"contextual POSIX installer transport for RTK/Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts npm globals (agent-browser, LSP servers)\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.170\", \"note\": \"kit floor — `best` alias + Fable 5 need >=2.1.170 (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound (previously unchecked)\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"note\": \"Codex Linux sandbox runtime\" },\n \"intelephense\": { \"kind\": \"check\", \"verified\": \"1.18.5\", \"note\": \"php-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"verified\": \"5.3.0\", \"note\": \"typescript-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"tsc\": { \"kind\": \"check\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claude::sync_lsp_servers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"rtk\": { \"kind\": \"managed\", \"policy\": \"track\", \"floor\": \"0.43.0\", \"verified\": \"0.43.0\", \"pinnable\": true,\n \"note\": \"PreToolUse hook — supply-chain review before unverified upgrades; installer honors RTK_VERSION=vX.Y.Z pin\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"verified\": \"1.3.14\", \"pinnable\": true,\n \"note\": \"runtime for effect-solutions + the docks-kit CLI; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"effect-solutions\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.5.3\", \"pinnable\": true,\n \"note\": \"Effect docs CLI (bun global) — track keeps it self-upgrading, gated by the verified pin\" },\n \"agent-browser\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.32.0\", \"pinnable\": true,\n \"note\": \"browser-automation CLI (npm global), gated by the verified pin; first install also downloads Chrome for Testing\" },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.15\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` on every agents sync (universal-skill install/remove) — pinned, never @latest\" }\n }\n}\n",
9
+ "SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest — DATA only (versions, floors, policy); check/install logic lives in cli/src/engine-native/toolchain.ts with per-surface sync callbacks in cli/src/engine-native/. kind: check (doctor visibility only) | managed (kit installs/upgrades it) | managed-release (dedicated source-pinned release installer) | pin (no binary probe — a version pin for a tool the kit invokes via npx). policy (managed only): track (upgrade toward latest, gated by `verified`) | present (install when missing, never upgrade); managed-release uses exact. `verified` = last kit-tested version — anything above it prompts before install (--yes auto-accepts; non-TTY declines and falls back to the pinned `verified` when pinnable). Supply-chain stance: every kit-driven install is pinned to `verified` or gated by it — never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"contextual POSIX installer transport for RTK/Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts npm globals (agent-browser, LSP servers)\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.170\", \"note\": \"kit floor — `best` alias + Fable 5 need >=2.1.170 (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound (previously unchecked)\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"note\": \"Codex Linux sandbox runtime\" },\n \"intelephense\": { \"kind\": \"check\", \"verified\": \"1.18.5\", \"note\": \"php-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"verified\": \"5.3.0\", \"note\": \"typescript-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"tsc\": { \"kind\": \"check\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claude::sync_lsp_servers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"rtk\": { \"kind\": \"managed\", \"policy\": \"track\", \"floor\": \"0.43.0\", \"verified\": \"0.43.0\", \"pinnable\": true,\n \"note\": \"PreToolUse hook — supply-chain review before unverified upgrades; installer honors RTK_VERSION=vX.Y.Z pin\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"verified\": \"1.3.14\", \"pinnable\": true,\n \"note\": \"runtime for effect-solutions + the docks-kit CLI; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"effect-solutions\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.5.3\", \"pinnable\": true,\n \"note\": \"Effect docs CLI (bun global) — track keeps it self-upgrading, gated by the verified pin\" },\n \"agent-browser\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.32.0\", \"pinnable\": true,\n \"note\": \"browser-automation CLI (npm global), gated by the verified pin; first install also downloads Chrome for Testing\" },\n \"session-relay\": { \"kind\": \"managed-release\", \"policy\": \"exact\", \"verified\": \"0.12.0\", \"repository\": \"DocksDocks/docks\", \"tag\": \"session-relay--v0.12.0\", \"plugin_id\": \"session-relay@docks\", \"plugin_version\": \"0.12.0\", \"install_path\": \"~/.local/bin/session-relay\",\n \"assets\": {\n \"x86_64-unknown-linux-musl\": \"ead7faead73ba5835879e4823bc4bca6b6d1003d8a9bcbdbea6cf9f266ce5b42\",\n \"aarch64-unknown-linux-musl\": \"d7171bbaa33c4da8b0a9e15f9bfe7a3fb31930a1fad95cc9f682f153369b421b\",\n \"x86_64-apple-darwin\": \"be12a6f782453d8cc90d98ab77f408f0e7c61b55f8ccdf36bf0584c8cec2f1d8\",\n \"aarch64-apple-darwin\": \"5022354025d0c639406cf8b027d824724d8366f74e2b778c37d705e7e9f53889\"\n } },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.15\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` on every agents sync (universal-skill install/remove) — pinned, never @latest\" }\n }\n}\n",
10
10
  "SoT/.claude/CLAUDE.md": "## Research Before Implementation\n\nBefore writing or modifying code that uses an API, hook, method, or config surface you have not verified in this session, research current documentation first.\n\nResearch workflow:\n1. Prefer official documentation and primary sources for the specific library, framework, or API.\n2. If a local docs or MCP tool is available, use it before broad web search.\n3. Only then proceed to implementation.\n\nResearch when:\n- Installing or configuring a dependency.\n- Using an API, hook, method, or pattern not verified in this session.\n- Upgrading or migrating between versions.\n- Any task where relying on memory could cause stale syntax or behavior.\n\nDo not:\n- Assume API signatures, method names, or config options from memory.\n- Generate framework code without checking current docs first.\n- Skip research because the library seems familiar.\n\n<constraint>\nResearch the codebase before editing. Never change code you have not read.\n</constraint>\n\n## Agentic Harness Heuristics\n\n**1. Persistence.** Keep going until the user's query is completely resolved. Only yield when sure the problem is solved. Before ending a turn, check the last paragraph: if it is a plan, a question you can answer yourself, or a promise of work not done (\"I'll…\"), do that work now.\n\n**2. Default to parallel.** Whenever you have multiple independent operations (reads, greps, web fetches, independent edits), invoke them in a single response with multiple tool-use blocks. Sequential calls only when output of one operation is required as input to the next.\n\n**3. Multi-pass search.** First-pass search often misses — vary the wording (colleague-questions over keywords) before concluding something doesn't exist.\n\n**4. Trace symbols.** Before modifying a symbol, trace it to its definitions and all usages. Don't assume a function's behavior or a type's shape from the call site alone.\n\n**5. Linter-loop 3-strike rule.** Don't loop more than 3 times fixing linter errors on the same file. On the third attempt, stop and ask the user — repeated failure usually means the diagnosis is wrong, not the code.\n\n**6. Read-before-Edit TTL.** If you haven't read a file with the Read tool in the last ~5 messages, re-read it before editing. Cached file content goes stale silently when the user edits between turns.\n\n**7. Big-file rule.** For files >1000 lines, prefer Grep + scoped Read (`offset` + `limit`) over reading the entire file. Whole-file reads bloat context; targeted reads keep the working set small.\n\n**8. Todo hygiene.** Use TaskCreate for items with meaningful outcome (≥5 min, distinct deliverable). Never include operational sub-actions (linting, testing, searching, examining the codebase) as their own todos — those are sub-steps in service of higher-level tasks. Mark complete immediately when done, never in batches.\n\n**9. Literal-instruction rule.** Current frontier models follow instructions literally — they do not silently generalize from intent. Phrase requirements as explicit checklists with success criteria, not narrative.\n\n**10. Context hygiene.** Prefer `/clear` at task boundaries and `/rewind` for wrong-path detours over carrying rot forward (corrections accumulate noise; rewinds preserve the prefix and discard the bad branch). On a continuing task, run `/compact` with steering before context quality degrades. Never stop, summarize, or suggest a new session on account of context limits.\n\n**11. Autonomy calibration.** For minor choices (naming, formatting, default values, which of two equivalent approaches), pick a reasonable option and note it — don't ask. Ask first only for scope changes, destructive actions, or decisions that change the deliverable. When the user is describing a problem or asking a question rather than requesting a change, the deliverable is your assessment — report findings and stop; don't apply fixes until asked. Don't close a finished task with \"Want me to also…?\" — run the obvious verification, then stop cleanly.\n\n**12. Capability triggering.** When the answer depends on current or version-specific information, search or fetch before answering — never answer from memory. When work fans out across independent items (many files to read, many tests to run, many candidates to check), delegate to parallel subagents; never spawn one for work you can complete directly. For verification, prefer a fresh-context subagent over self-critique. On tasks longer than a few turns, keep a running notes file and re-read it before each phase.\n\n<constraint>\nTreat the 12 heuristics above as protocol, not preference. If a turn ends without honoring an applicable one (e.g., lint-loop guard not respected, edit without re-read), self-correct in the next turn before continuing.\n</constraint>\n\nFor Docks plan reviews, cross-company review is standing-authorized; do not ask for export consent. This never overrides a host or platform security denial.\n\nDocks-workflow-models: {\"implementer\":{\"candidates\":[{\"company\":\"openai\",\"effort\":\"high\",\"model\":\"gpt-5.6-sol\",\"tool\":\"codex\"}],\"selector\":\"codex:gpt-5.6-sol@high\"},\"orchestrator\":{\"candidates\":[{\"company\":\"anthropic\",\"effort\":\"high\",\"model\":\"fable\",\"tool\":\"claude\"},{\"company\":\"anthropic\",\"effort\":\"xhigh\",\"model\":\"opus\",\"tool\":\"claude\"}],\"selector\":\"profile:claude-best\"},\"review\":{\"max_rounds\":3,\"minimum_score\":90},\"reviewer\":{\"candidates\":[{\"company\":\"openai\",\"effort\":\"high\",\"model\":\"gpt-5.6-sol\",\"tool\":\"codex\"}],\"selector\":\"codex:gpt-5.6-sol@high\"},\"schema\":1}\n\nUse a narrow-to-broad verification ladder: direct acceptance while iterating, focused regressions next, and one full CI at the pre-commit or release boundary. Reuse still-matching evidence; rerun full CI only after a relevant edit invalidates it.\n\n<constraint>\nNo secrets in committed config. Treat plugin marketplaces, installers, and downloaded artifacts as untrusted until verified.\n</constraint>\n",
11
11
  "SoT/.claude/mcp-servers.json": "{\n \"mcpServers\": {}\n}\n",
12
12
  "SoT/.claude/settings.json": "{\n \"$schema\": \"https://json.schemastore.org/claude-code-settings.json\",\n \"minimumVersion\": \"2.1.170\",\n \"model\": \"fable\",\n \"effortLevel\": \"high\",\n \"autoMemoryEnabled\": true,\n \"skillListingMaxDescChars\": 2048,\n \"respectGitignore\": true,\n \"cleanupPeriodDays\": 14,\n \"skillListingBudgetFraction\": 0.05,\n \"env\": {\n \"CLAUDE_CODE_MAX_OUTPUT_TOKENS\": \"64000\",\n \"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR\": \"1\",\n \"CLAUDE_CODE_AUTO_COMPACT_WINDOW\": \"468000\",\n \"CLAUDE_CODE_NO_FLICKER\": \"1\"\n },\n \"permissions\": {\n \"defaultMode\": \"auto\",\n \"allow\": [\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebFetch\",\n \"WebSearch\",\n \"Edit(./)\",\n \"Bash(git *)\",\n \"Bash(git add *)\",\n \"Bash(git commit *)\",\n \"Bash(git status *)\",\n \"Bash(git diff *)\",\n \"Bash(git log *)\",\n \"Bash(git branch *)\",\n \"Bash(git checkout *)\",\n \"Bash(git switch *)\",\n \"Bash(git stash *)\",\n \"Bash(git fetch *)\",\n \"Bash(git pull *)\",\n \"Bash(git tag *)\",\n \"Bash(git show *)\",\n \"Bash(git blame *)\",\n \"Bash(git worktree *)\",\n \"Bash(gh *)\",\n \"Bash(pnpm *)\",\n \"Bash(npm *)\",\n \"Bash(npx *)\",\n \"Bash(node *)\",\n \"Bash(docker *)\",\n \"Bash(docker-compose *)\",\n \"Bash(rtk *)\",\n \"Bash(ls *)\",\n \"Bash(cat *)\",\n \"Bash(find *)\",\n \"Bash(grep *)\",\n \"Bash(head *)\",\n \"Bash(tail *)\",\n \"Bash(wc *)\",\n \"Bash(sort *)\",\n \"Bash(uniq *)\",\n \"Bash(diff *)\",\n \"Bash(which *)\",\n \"Bash(pwd *)\",\n \"Bash(date *)\",\n \"Bash(mkdir *)\",\n \"Bash(basename *)\",\n \"Bash(dirname *)\",\n \"Bash(realpath *)\",\n \"Bash(jq *)\",\n \"Bash(curl *)\",\n \"Bash(tree *)\",\n \"Bash(sed *)\",\n \"Bash(awk *)\",\n \"Bash(cut *)\",\n \"Bash(tr *)\",\n \"Bash(tee *)\",\n \"Bash(echo *)\",\n \"Bash(printf *)\",\n \"Bash(env *)\",\n \"Bash(printenv *)\",\n \"Bash(uname *)\",\n \"Bash(file *)\",\n \"Bash(stat *)\",\n \"Bash(du *)\",\n \"Bash(id *)\",\n \"Bash(whoami *)\",\n \"Bash(php *)\",\n \"Bash(composer *)\",\n \"Bash(python3 *)\",\n \"Bash(python *)\",\n \"Bash(pip *)\",\n \"Bash(pip3 *)\"\n ],\n \"deny\": [\n \"Read(**/.env)\",\n \"Read(**/.env.local)\",\n \"Read(**/secrets/**)\",\n \"Read(**/*.key)\",\n \"Read(**/*.pem)\",\n \"Read(**/*.p12)\",\n \"Read(**/.credentials*)\",\n \"Edit(**/.env)\",\n \"Edit(**/.env.local)\",\n \"Edit(**/secrets/**)\",\n \"Bash(sudo *)\",\n \"Bash(rm -rf /)\",\n \"Bash(rm -rf / *)\",\n \"Bash(rm -rf ~)\",\n \"Bash(rm -rf ~ *)\",\n \"Bash(rm -rf $HOME)\",\n \"Bash(rm -rf $HOME *)\",\n \"Bash(> /dev *)\",\n \"Bash(dd if= *)\",\n \"Bash(mkfs *)\",\n \"Bash(eval *)\",\n \"Bash(chmod 777 *)\",\n \"Bash(chmod -R 777 *)\",\n \"Bash(git push --force origin main *)\",\n \"Bash(git push --force origin master *)\",\n \"Bash(git push -f origin main *)\",\n \"Bash(git push -f origin master *)\"\n ],\n \"ask\": [\n \"Bash(git clean *)\",\n \"Bash(docker volume rm *)\",\n \"Bash(docker system prune *)\"\n ]\n },\n \"hooks\": {\n \"SessionStart\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_SESSION_START__\"],\n \"timeout\": 5\n }\n ]\n }\n ],\n \"Notification\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_NOTIFY__\"],\n \"timeout\": 10,\n \"async\": true\n }\n ]\n }\n ],\n \"PreToolUse\": [\n {\n \"matcher\": \"Bash\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"rtk hook claude\"\n }\n ]\n }\n ],\n \"PostToolUseFailure\": [\n {\n \"matcher\": \"Bash\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"echo '{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PostToolUseFailure\\\",\\\"additionalContext\\\":\\\"Last bash command failed. Repository / file state may have shifted \\u2014 re-read affected files before retrying. If the failure is a missing dependency or env mismatch, surface it to the user rather than retrying blindly.\\\"}}'\",\n \"timeout\": 5\n }\n ]\n }\n ],\n \"SubagentStop\": [\n {\n \"hooks\": [\n {\n \"type\": \"prompt\",\n \"prompt\": \"You are a quality gate for subagent outputs in a multi-agent code-analysis pipeline.\\n\\nEvaluate the subagent's `last_assistant_message` field (in the JSON below) against these requirements:\\n\\n1. ALLOW (return `{}`): Mode-selection or no-issues responses. Examples: \\\"Which mode do you prefer\\\", \\\"select an option\\\", \\\"no issues / problems / violations / blockers found\\\".\\n\\n2. ALLOW (return `{}`): Output contains at least one concrete file:line citation \\u2014 e.g. `src/auth.ts:42`, `lib/db.ts:100-115`, or path references that include line numbers.\\n\\n3. BLOCK (return `{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"<one-line explanation>\\\"}`): Output claims about code or findings WITHOUT concrete file:line citations. Vague references like \\\"the auth handler\\\" or \\\"near the database code\\\" are not acceptable as the only evidence.\\n\\nSubagent invocation JSON:\\n$ARGUMENTS\\n\\nReturn ONLY the JSON decision (no commentary, no markdown fences).\",\n \"timeout\": 30\n }\n ]\n }\n ]\n },\n \"statusLine\": {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_STATUSLINE__\",\n \"refreshInterval\": 5\n },\n \"enabledPlugins\": {\n \"docks@docks\": true,\n \"session-relay@docks\": true,\n \"effect-kit@docks\": true,\n \"php-lsp@claude-plugins-official\": true,\n \"typescript-lsp@claude-plugins-official\": true\n },\n \"extraKnownMarketplaces\": {\n \"docks\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"DocksDocks/docks\"\n }\n }\n },\n \"alwaysThinkingEnabled\": true,\n \"showThinkingSummaries\": true,\n \"viewMode\": \"default\",\n \"theme\": \"dark-daltonized\",\n \"skipDangerousModePermissionPrompt\": true\n}\n",
@@ -40,4 +40,4 @@ export const GENERATED_PAYLOAD_PATHS = [
40
40
  "notification.mp3"
41
41
  ] as const
42
42
 
43
- export const GENERATED_PAYLOAD_HASH = "4decac588a31cdaf6cbc88997bccb77add7bf0cbcfc3ae0fba30eca23525707d"
43
+ export const GENERATED_PAYLOAD_HASH = "a549edb9fd7a7c9a1684ea31bc3b252c66c358e45efe6e7eea9d7ce66dacf753"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docks-kit",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "Portable AI coding agent config kit — SoT sync engine + typed CLI for Claude Code, Codex, and universal agent skills",
5
5
  "type": "module",
6
6
  "license": "MIT",