@loicngr/kobo 2.1.20 → 2.1.22
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 +37 -4
- package/CHANGELOG.md +9 -0
- package/CONFIGURATION.md +42 -5
- package/dist/server/services/agent/engines/claude-code/engine.js +5 -2
- package/dist/server/services/agent/engines/codex/engine.js +3 -0
- package/dist/server/services/agent/orchestrator.js +7 -0
- package/dist/server/services/agent/turn-liveness.js +1 -1
- package/dist/server/services/settings-service.js +30 -0
- package/docker-compose.vps.yml +9 -3
- package/docs/deployment-vps.md +153 -34
- package/docs/getting-started.md +16 -0
- package/package.json +1 -1
- package/scripts/kobo-install +35 -8
- package/scripts/kobo-update +37 -15
- package/src/client/dist/pwa/assets/{ActivityFeed-DiUxnmGJ.js → ActivityFeed-2wu6qXx1.js} +1 -1
- package/src/client/dist/pwa/assets/{AutoLoopFinalReviewSettings-f4VDR5dd.js → AutoLoopFinalReviewSettings-CwnNUWCh.js} +1 -1
- package/src/client/dist/pwa/assets/{ChangelogPage-dEWpjTlp.js → ChangelogPage-D9ke0TYz.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-Bwbqgvr4.js → ComparisonPanel-ytDu1L7a.js} +1 -1
- package/src/client/dist/pwa/assets/{CreatePage-CCCCAhrx.js → CreatePage-YQ_jGilf.js} +1 -1
- package/src/client/dist/pwa/assets/{DashboardPage-3byhBITf.js → DashboardPage-C-PyArla.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-Bj_NTfCF.js → DiffViewer-CPobwSwm.js} +3 -3
- package/src/client/dist/pwa/assets/{DocumentsPanel-xyLDV3T1.js → DocumentsPanel-BjZGNI-p.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-pwBSstQP.js → DrawerToggleButton-Dtd6fhYu.js} +1 -1
- package/src/client/dist/pwa/assets/{GitPanel-DvIdrkVw.js → GitPanel-BHJ3lpJZ.js} +3 -3
- package/src/client/dist/pwa/assets/{HealthPage-BSwhZQRo.js → HealthPage-Lv_IdF1l.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-DZJ9BvZw.js → MainLayout-DYumlWcP.js} +2 -2
- package/src/client/dist/pwa/assets/{MemoryPanel-ChR032zP.js → MemoryPanel-DS2F3x-S.js} +1 -1
- package/src/client/dist/pwa/assets/{MemoryProposalList-BRKSoS4K.js → MemoryProposalList-D_KDS3uX.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnDropdown-o3QH0Bb8.js → QBtnDropdown-XkBxmJO9.js} +1 -1
- package/src/client/dist/pwa/assets/{QExpansionItem-BhFHiyq9.js → QExpansionItem-B5vbHxJ2.js} +1 -1
- package/src/client/dist/pwa/assets/{QScrollArea-BsNY6slB.js → QScrollArea-BE_fAg20.js} +1 -1
- package/src/client/dist/pwa/assets/{QSelect-bEq3g_io.js → QSelect-BjZk4-r8.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-xYZ6KNu2.js → QTooltip-Dhn6VXWR.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-DIFv_FTY.js → SchedulePanel-BDhIt9pX.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-BqTBIt5E.js → SearchPage-CJrtIenZ.js} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-BSwN45zt.js +9 -0
- package/src/client/dist/pwa/assets/{SettingsPage-BRvUAZ5r.css → SettingsPage-CNIpxG-U.css} +1 -1
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-D6r2pl5g.js → SplitWorkspacePage-BV62otQw.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-CladK9Vv.js → TerminalPanel-TQUHtZ-q.js} +1 -1
- package/src/client/dist/pwa/assets/{ToolCallItem-CdzEME4w.js → ToolCallItem-DxdYefuz.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-B4gQPwGN.js → TouchPan-CtwfZgkX.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-B0j6AZwU.js → TourReplayButton-DEC_Yt6E.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-CeVhoidC.js → WorkspaceAttentionLabels-BehGsWhI.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspacePage-w2bF8wTd.css → WorkspacePage-Bk-WWOYM.css} +1 -1
- package/src/client/dist/pwa/assets/{WorkspacePage-CK5XCuG_.js → WorkspacePage-CQz6QL5L.js} +3 -3
- package/src/client/dist/pwa/assets/{browser-B8tNScg1.js → browser-B3bGJv7H.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-kmVAcvs_.js → build-path-tree-Bwwe_8vU.js} +1 -1
- package/src/client/dist/pwa/assets/{custom-sounds-CJwd-8TL.js → custom-sounds-1Rb1a5LA.js} +1 -1
- package/src/client/dist/pwa/assets/{de-BaO6kbdO.js → de-bfOiFUu1.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-CgbPVW9s.js → editor.api-D2PTZoSF.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-y7Wf_Rgd.js → efforts-JkWc_mU5.js} +1 -1
- package/src/client/dist/pwa/assets/{es-DTvSsbD0.js → es-C5Sk8pec.js} +1 -1
- package/src/client/dist/pwa/assets/{expand-template-BFbbjj7X.js → expand-template-C-aGZz7k.js} +1 -1
- package/src/client/dist/pwa/assets/{format-BETfc4z9.js → format-BbXB3nKS.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-C22K9vlC.js → formatters-Bu3lC0Jk.js} +1 -1
- package/src/client/dist/pwa/assets/{fr-BH7V66Yz.js → fr-BNVJp67K.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-DiRVvp9O.js → freemarker2-qq6O820q.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-BLYRroSQ.js → handlebars-nXATCa7t.js} +1 -1
- package/src/client/dist/pwa/assets/{html-DAW2vteY.js → html-ZSNyTQeY.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-B_b02gtS.js +1 -0
- package/src/client/dist/pwa/assets/{index-CbyHfF8I.js → index-BZKHE14f.js} +3 -3
- package/src/client/dist/pwa/assets/{it-DZGbkkoy.js → it-B9g24VRZ.js} +1 -1
- package/src/client/dist/pwa/assets/{javascript-Bb5J8n0k.js → javascript-B3Elzrc0.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Dqx3adnJ.js → liquid-CrMGh9mA.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-D_7igLV3.js → mdx-C6nxSGQ5.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-C-0CbrAy.js → monaco-contributions-WtuTHJJ8.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-c6JPBEZI.js → monaco.contribution--KeSH1BU.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-BE4qbvWm.js → network-auth-DknPaiQS.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-BVFBBGrJ.js → notify-theme-DTaB0RsK.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-CHOR2jB5.js → project-color-bm_jpHka.js} +1 -1
- package/src/client/dist/pwa/assets/{python-Dqubw1F5.js → python-CQI_FwfK.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-BQphc9Kt.js → razor-BT2wh4_7.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-Dep00l0X.js → typescript-D3ibItMf.js} +1 -1
- package/src/client/dist/pwa/assets/{use-search-index-status-CQHHvHwM.js → use-search-index-status-BnFR-_2l.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-IVbXTmz2.js → use-tours-CsPhvrxf.js} +2 -2
- package/src/client/dist/pwa/assets/{use-worktree-restore-B3s5xDGR.js → use-worktree-restore-B5s0BuHk.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-CyuTpztP.js → workspace-templates-GLO1mBlj.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-BMS2dvaF.js → xml-EIEQlO3Y.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-CYw92aic.js → yaml-CJdHLr6G.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-BLD0xWFB.js +0 -9
- package/src/client/dist/pwa/assets/i18n-DnuTmw3O.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -201,6 +201,10 @@ Every feature that touches the schema:
|
|
|
201
201
|
|
|
202
202
|
## WebSocket protocol
|
|
203
203
|
|
|
204
|
+
Global `showProgressBars` defaults to true (settings migration v65). The panel
|
|
205
|
+
honors this preference without resetting its store or dismissing rows: MCP writes,
|
|
206
|
+
revisions and success expiry continue while hidden.
|
|
207
|
+
|
|
204
208
|
Session progress bars use the separate `progress:changed` ephemeral event with a
|
|
205
209
|
public record payload and workspace/session envelope. Persist the row before
|
|
206
210
|
broadcasting; never append numeric updates to chat history or overload native
|
|
@@ -233,7 +237,7 @@ locally queued or unacknowledged messages still block `turn:completed` and
|
|
|
233
237
|
input closure. UUID-less legacy results retain a conservative one-input
|
|
234
238
|
fallback for already yielded inputs only; a result never credits a future send.
|
|
235
239
|
Backpressure caps unacknowledged yielded inputs at 64, matching the SDK's result
|
|
236
|
-
UUID list bound. Keep the inactivity watchdog for genuinely unanswered work.
|
|
240
|
+
UUID list bound. Keep the opt-in inactivity watchdog for genuinely unanswered work.
|
|
237
241
|
|
|
238
242
|
Claude SDK `task_*` messages cover agents **and** background jobs. Preserve `task_type` by task/tool identity across progress and terminal messages, which often omit it. The existing `subagent:progress` envelope also carries this lifecycle metadata for compatibility; `local_bash`, `local_workflow` and `mcp_task` must be excluded from agent cards, busy-agent counts, chat agent-name resolution and the selective stop-subagents action. Keep tracking these jobs for engine liveness and whole-session interruption. Legacy/unknown classifications and Codex agents remain supported; never classify from a description or from the agent's last tool (`Bash` can be a real agent's current action).
|
|
239
243
|
|
|
@@ -282,6 +286,21 @@ Traefik network. It persists `/root`, mounts project paths identically on host a
|
|
|
282
286
|
container, and leaves the Docker socket opt-in. `docs/deployment-vps.md` owns the
|
|
283
287
|
operator walkthrough; `CONFIGURATION.md` compares all Compose topologies.
|
|
284
288
|
|
|
289
|
+
The VPS launcher `scripts/kobo-vps-entrypoint` bootstraps the dedicated home and
|
|
290
|
+
executes Kōbō as positive `KOBO_UID:KOBO_GID`, adding the socket's numeric group
|
|
291
|
+
when mounted. Keep `HOME=/root` and the existing volume for compatibility. Do not
|
|
292
|
+
chown existing project mounts, follow home symlinks, or resolve root bootstrap
|
|
293
|
+
commands through user-writable skill/tool paths. `--exec` runs operator commands
|
|
294
|
+
as the agent without rescanning ownership. Legacy root Compose entrypoints are
|
|
295
|
+
preserved by the updater and need the documented explicit migration. New installer
|
|
296
|
+
identity fields must not silently reconfigure old completed manifests.
|
|
297
|
+
|
|
298
|
+
The updater needs the launcher beside its script to build its compatibility layer
|
|
299
|
+
(including netcat/Buildx). `docs/vps-projects-and-skills.md` covers dependency/env
|
|
300
|
+
setup, private registry auth, SSH dev-server access, scoped cleanup, native
|
|
301
|
+
Claude/Codex skills and persistence. Project-specific OS packages and host inotify
|
|
302
|
+
changes are operator-managed; the updater does not preserve arbitrary image layers.
|
|
303
|
+
|
|
285
304
|
`scripts/kobo-install` guides a fresh installation; it does not adopt existing
|
|
286
305
|
unmanaged stacks. Preserve its read-only `--check`, explicit Docker-socket opt-in,
|
|
287
306
|
managed-file fingerprints, shared updater lock, and durable start phases. Completed
|
|
@@ -300,13 +319,20 @@ credentials into an image or add web settings for these host-managed secrets.
|
|
|
300
319
|
|
|
301
320
|
`scripts/kobo-update` is a Python 3.10+ operator tool for this layout, separate
|
|
302
321
|
from the backend's update notifications. Preserve explicit invocation, idle checks,
|
|
303
|
-
unique image tags, private stopped
|
|
322
|
+
unique image tags, private backups of `/root/.config/kobo` while stopped, migration tests on a copy,
|
|
304
323
|
and durable failure records. The authenticated `/api/maintenance/update` reservation
|
|
305
324
|
checks runtime ownership and closes agent admission synchronously before shutdown.
|
|
306
325
|
Track in-flight workspace creations and PR checkouts through all asynchronous
|
|
307
326
|
steps and rollback. Maintenance and creation admission must exclude each other
|
|
308
327
|
synchronously; rejecting only the final agent start is too late. Direct HTTP and
|
|
309
328
|
MCP creation paths share this guard.
|
|
329
|
+
The updater's `kobo.tar.gz` contains only Kōbō data, with paths relative to `/root`;
|
|
330
|
+
never archive engine homes, skills, caches or projects as part of routine updates.
|
|
331
|
+
Record the archive name/scope in update state. Migration checks extract regular
|
|
332
|
+
SQLite files and sidecars only. Recovery must preserve the original volume and
|
|
333
|
+
clone it before replacing only `.config/kobo` in the clone; a partial data archive
|
|
334
|
+
must not be described as a complete home snapshot. Old `home.tar.gz` jobs retain
|
|
335
|
+
their original recovery instructions.
|
|
310
336
|
Scheduled cron/wakeup/quota deliveries defer without consuming persisted intent;
|
|
311
337
|
valid pending wakeups get `retry_at=target_at` before successful reservation (only
|
|
312
338
|
when `retry_at` was null), so long maintenance does not expire them at boot. Keep
|
|
@@ -372,11 +398,18 @@ through `ExitPlanMode`; a permission change requires a new launch.
|
|
|
372
398
|
|
|
373
399
|
Two engines live under `src/server/services/agent/engines/`, both implementing the `AgentEngine` contract in `types.ts`:
|
|
374
400
|
|
|
375
|
-
|
|
401
|
+
Global `agentWatchdogEnabled` is opt-in (false for fresh and existing installs;
|
|
402
|
+
settings migration v64). The orchestrator snapshots it into `StartOptions.watchdogEnabled`
|
|
403
|
+
on every launch/resume for both engines. Gate stream/tool inactivity, background
|
|
404
|
+
stall, compaction and failed-result-drain watchdogs; retain normal completion,
|
|
405
|
+
manual stop, protocol/startup deadlines and confirmed-dead-process cleanup.
|
|
406
|
+
The toggle is first in Settings → Agents and does not reconfigure active launches.
|
|
407
|
+
|
|
408
|
+
**Claude Code** (`claude-code/`): consumes the `@anthropic-ai/claude-agent-sdk` async iterator. The SDK manages the Claude runtime; Kōbō does not launch a separate `claude` CLI command. Authentication reuses the user's Claude login or `ANTHROPIC_API_KEY`. A settled result gets a **3 s continuation grace**, then an opt-in **15 s result-drain watchdog**. Trailing metadata and child output must not cancel the parent's drain/grace. When watchdogs are enabled, silence is bounded at **2 minutes** without tools, **30 minutes** for foreground tools and background shell/workflow/MCP jobs, and **10 minutes** for agents. Compaction has an absolute **10-minute** ceiling; repeated status messages cannot extend it. Human questions/permissions pause inactivity; resolution AND SDK cancellation re-evaluate that pause. Watchdogs request abort plus SDK `query.close()`; neither is proof of closure: controller ownership is released only when `EngineProcess.closed` confirms it. Never fake closure or allow a second writer if the SDK ignores termination. End emission is idempotent and timers are cleared on termination and in `finally`.
|
|
376
409
|
|
|
377
410
|
After the continuation grace, Claude explicitly calls `query.close()`; closing
|
|
378
411
|
the input iterator alone can leave the SDK waiting for an idle notification after
|
|
379
|
-
a successful result. The 15-second drain watchdog is a failed-close backstop,
|
|
412
|
+
a successful result. The opt-in 15-second drain watchdog is a failed-close backstop,
|
|
380
413
|
not normal cleanup. The SDK still constructs its runtime command; Kōbō's
|
|
381
414
|
`spawnClaudeCodeProcess` hook tracks actual child exit and forwards stderr.
|
|
382
415
|
`EngineProcess.closed` waits for both that exit and iterator completion because
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to Kōbō are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/). Each release is an `## <version>`
|
|
5
5
|
section — the in-app "What's new" dialog reads this file.
|
|
6
6
|
|
|
7
|
+
## 2.1.22
|
|
8
|
+
|
|
9
|
+
- feat(settings): make agent watchdogs opt-in and progress bars configurable
|
|
10
|
+
- feat(update): refine `kobo-update` to backup only `.config/kobo` and improve migration handling
|
|
11
|
+
|
|
12
|
+
## 2.1.21
|
|
13
|
+
|
|
14
|
+
- feat(vps): introduce non-root runtime launcher and UID/GID support
|
|
15
|
+
|
|
7
16
|
## 2.1.20
|
|
8
17
|
|
|
9
18
|
- fix(progress): clarify activity updates and completion
|
package/CONFIGURATION.md
CHANGED
|
@@ -859,6 +859,12 @@ External senders can supply `idempotency_key` once per intended message and reus
|
|
|
859
859
|
|
|
860
860
|
## Session progress bars
|
|
861
861
|
|
|
862
|
+
**Settings → Agents → Show progress bars** (`showProgressBars`) is enabled by
|
|
863
|
+
default. Saving this global preference hides or restores the panel immediately in
|
|
864
|
+
the current client; other clients receive it on their next settings refresh.
|
|
865
|
+
Hiding the panel does not stop reporting, dismiss activities or reset their
|
|
866
|
+
success expiry timer. Individually dismissed bars stay dismissed when re-enabled.
|
|
867
|
+
|
|
862
868
|
Agents can report long-running tests, builds or reviews through the
|
|
863
869
|
`create_progress_bar`, `update_progress_bar`, `finish_progress_bar` and
|
|
864
870
|
`list_progress_bars` MCP tools. These tools are available through the workspace
|
|
@@ -1183,17 +1189,20 @@ Traefik already exists. Build the image from a source checkout before starting.
|
|
|
1183
1189
|
All four use the same underlying image (`docker build -t kobo:local .` from the
|
|
1184
1190
|
repo root). The original three mount provider/Git configuration from the host;
|
|
1185
1191
|
the VPS example keeps independent credentials in a persistent container home.
|
|
1186
|
-
The VPS example
|
|
1192
|
+
The VPS example uses `kobo-vps-entrypoint`: it prepares the dedicated home volume
|
|
1193
|
+
then drops to `KOBO_UID:KOBO_GID` before starting Node, without `sshd`. The original
|
|
1187
1194
|
files use the image entrypoint, which starts `sshd` even when port 22 is not published.
|
|
1195
|
+
See the [VPS runtime user and migration procedure](./docs/deployment-vps.md#runtime-user-and-existing-installations)
|
|
1196
|
+
and [project/skills operations guide](./docs/vps-projects-and-skills.md).
|
|
1188
1197
|
|
|
1189
1198
|
### The image
|
|
1190
1199
|
|
|
1191
1200
|
Multi-stage `Dockerfile`, `node:24.19.0-slim` base:
|
|
1192
1201
|
|
|
1193
1202
|
- **Build stage**: installs root, client, and PWA deps, then runs `npm run build` (client via Quasar, server via `tsc`).
|
|
1194
|
-
- **Runtime stage**: production deps only, plus OS tooling the agents and forge CLIs need — `git`, `openssh-client` + `openssh-server`, `ripgrep`, `fd-find`, `jq`, `curl`, Python + build tools, the Docker CLI and
|
|
1203
|
+
- **Runtime stage**: production deps only, plus OS tooling the agents and forge CLIs need — `git`, `openssh-client` + `openssh-server`, `ripgrep`, `fd-find`, `jq`, `curl`, Python + build tools, the Docker CLI, Compose and Buildx plugins (talk to a mounted host socket, see [Volumes](#volumes)), netcat (`nc`), `gh`, `glab`, `tmux`, `vim`/`nano`, and more for interactive shell use.
|
|
1195
1204
|
- **Global CLIs**: `@anthropic-ai/claude-code` and `@openai/codex` are installed globally for interactive login (`claude /login`, `codex login` over SSH) — separate from Kōbō's own bundled SDK/subprocess usage, which needs no global install.
|
|
1196
|
-
- **`git config --system --add safe.directory '*'`**:
|
|
1205
|
+
- **`git config --system --add safe.directory '*'`**: legacy Compose examples run as root while mounted repos retain the host UID; the VPS example instead uses a matching non-root UID. The image still pre-trusts mounted Git repositories. This broad trust is for a single-user environment, not a multi-tenant host.
|
|
1197
1206
|
- **`HEALTHCHECK`**: `curl -f http://localhost:$SERVER_PORT/api/health` every 30s. `/api/health` is exempt from the network-access token gate unconditionally (even with `networkAccessBehindProxy` on) — Docker's `HEALTHCHECK` process has no way to supply the runtime-generated token, and without the exemption a "behind a reverse proxy" container never reports healthy, which makes Traefik's Docker provider filter it out of its router table entirely (Traefik drops unhealthy/starting containers from routing).
|
|
1198
1207
|
- **`EXPOSE 3000`**, `ENV SERVER_PORT=3000`, entrypoint `docker-entrypoint.sh` → `npm start`.
|
|
1199
1208
|
|
|
@@ -1201,6 +1210,15 @@ Build once: `docker build -t kobo:local .`
|
|
|
1201
1210
|
|
|
1202
1211
|
### Environment variables (Docker)
|
|
1203
1212
|
|
|
1213
|
+
The VPS template accepts `KOBO_UID` and `KOBO_GID` (positive numeric IDs, default
|
|
1214
|
+
1000); the installer defaults to the invoking host user's IDs. It retains
|
|
1215
|
+
`HOME=/root` for volume compatibility and adds the mounted Docker socket's group
|
|
1216
|
+
when applicable. Use `docker compose exec kobo kobo-vps-entrypoint --exec <command>`
|
|
1217
|
+
for logins, skills and project tooling as that user. It also sets
|
|
1218
|
+
`NODE_ENV=development` and `NPM_CONFIG_INCLUDE=dev` for project subprocesses;
|
|
1219
|
+
Kōbō continues serving its prebuilt UI/server. The base image retains its
|
|
1220
|
+
production default for other deployment layouts.
|
|
1221
|
+
|
|
1204
1222
|
Beyond the [general environment variables](#environment-variables), these matter specifically for a container deployment:
|
|
1205
1223
|
|
|
1206
1224
|
| Variable | Where it's set | Purpose |
|
|
@@ -1353,6 +1371,9 @@ For the independent VPS layout, [`scripts/kobo-update`](./scripts/kobo-update)
|
|
|
1353
1371
|
accepts a stable version or defaults to npm `latest`. It has `--check`, `--prepare`
|
|
1354
1372
|
and `--directory` options. See [updater usage and recovery](./docs/deployment-vps.md#update-with-kobo-update).
|
|
1355
1373
|
This is an operator-invoked script; the in-app update banner does not install releases.
|
|
1374
|
+
Its `kobo.tar.gz` backup contains only `/root/.config/kobo` (database, settings and
|
|
1375
|
+
other Kōbō data). The rest of the persistent home, including engine credentials,
|
|
1376
|
+
skills and caches, is preserved in place but is not included in this backup.
|
|
1356
1377
|
|
|
1357
1378
|
A source-built image needs rebuilding for a new release; `docker compose pull`
|
|
1358
1379
|
alone cannot update an image that only exists locally. Recreate the service
|
|
@@ -1404,15 +1425,27 @@ The model selector is populated from [`src/shared/models.ts`](./src/shared/model
|
|
|
1404
1425
|
|
|
1405
1426
|
#### Session watchdogs
|
|
1406
1427
|
|
|
1428
|
+
**Settings → Agents → Enable agent inactivity watchdogs**, at the top of the tab,
|
|
1429
|
+
controls the global `agentWatchdogEnabled` boolean. It is **off by default**, for
|
|
1430
|
+
both new and existing installations. Each Claude/Codex launch or resume captures
|
|
1431
|
+
the current value; saving the toggle does not interrupt or reconfigure an already
|
|
1432
|
+
running agent. Stop and resume that agent to apply the new value.
|
|
1433
|
+
|
|
1434
|
+
When off, Kōbō does not force-close a session because of stream/tool silence,
|
|
1435
|
+
background agents/jobs, stalled compaction, or a stalled result drain. Long silent
|
|
1436
|
+
CI monitoring can continue. Normal completion, manual Stop, provider failures,
|
|
1437
|
+
protocol request/startup deadlines and cleanup of confirmed dead processes still
|
|
1438
|
+
work; this switch only controls the engine watchdog deadlines described below.
|
|
1439
|
+
|
|
1407
1440
|
Kōbō distinguishes a finished response from a fully closed SDK session. Once a
|
|
1408
1441
|
response is settled, it allows 3 seconds for continuation, then explicitly closes
|
|
1409
1442
|
the SDK query. Normal cleanup is reported as completion, not a watchdog failure.
|
|
1410
|
-
|
|
1443
|
+
When watchdogs are enabled, a 15-second drain watchdog remains if that close fails to end the iterator.
|
|
1411
1444
|
Metadata alone cannot extend this cleanup deadline. Writer ownership waits for
|
|
1412
1445
|
both iterator completion and confirmed runtime exit, even if the SDK's own
|
|
1413
1446
|
bounded cleanup returns earlier.
|
|
1414
1447
|
|
|
1415
|
-
|
|
1448
|
+
With watchdogs enabled, silence limits are 2 minutes without a pending tool, 10 minutes for background
|
|
1416
1449
|
agents, and 30 minutes for tools (including background Bash, workflow and MCP
|
|
1417
1450
|
jobs). A compaction has a separate absolute 10-minute limit. Waiting for your
|
|
1418
1451
|
answer or permission pauses the inactivity watchdog.
|
|
@@ -1482,6 +1515,10 @@ rules survive restarts and can be removed from the workspace's permission UI.
|
|
|
1482
1515
|
|
|
1483
1516
|
Each workspace can run its own dev server. The **Tools** panel shows its status, a clickable URL, the live container count, and a logs button. That works only when your project follows a small contract. This section documents the contract so the panel lights up for *your* project, not just Docker-instance projects.
|
|
1484
1517
|
|
|
1518
|
+
For remote installations, `localhost` in that link is the browser's computer.
|
|
1519
|
+
See [VPS dev-server access](./docs/vps-projects-and-skills.md#reach-a-development-server-from-your-pc)
|
|
1520
|
+
for SSH forwarding, separate proxy routes and scoped stop/cleanup commands.
|
|
1521
|
+
|
|
1485
1522
|
### What the panel actually does
|
|
1486
1523
|
|
|
1487
1524
|
Kōbō treats the dev server in two independent layers:
|
|
@@ -565,6 +565,7 @@ export function createClaudeCodeEngine() {
|
|
|
565
565
|
// first SDK message, re-armed on meaningful progress, suspended while a human
|
|
566
566
|
// decision or a background subagent is outstanding, stopped in `finally`.
|
|
567
567
|
const turnLiveness = createTurnLiveness({
|
|
568
|
+
enabled: options.watchdogEnabled === true,
|
|
568
569
|
timeoutMs: CLAUDE_STREAM_IDLE_TIMEOUT_MS,
|
|
569
570
|
onTimeout() {
|
|
570
571
|
// The armed deadline tracks whether a foreground tool call is in
|
|
@@ -618,7 +619,7 @@ export function createClaudeCodeEngine() {
|
|
|
618
619
|
compactionStallTimer = undefined;
|
|
619
620
|
};
|
|
620
621
|
const armCompactionStallTimer = () => {
|
|
621
|
-
if (compactionStallTimer)
|
|
622
|
+
if (!options.watchdogEnabled || compactionStallTimer)
|
|
622
623
|
return;
|
|
623
624
|
compactionStallTimer = setTimeout(() => {
|
|
624
625
|
compactionStallTimer = undefined;
|
|
@@ -639,7 +640,7 @@ export function createClaudeCodeEngine() {
|
|
|
639
640
|
compactionStallTimer.unref?.();
|
|
640
641
|
};
|
|
641
642
|
const armResultDrainWatchdog = () => {
|
|
642
|
-
if (resultDrainTimer)
|
|
643
|
+
if (!options.watchdogEnabled || resultDrainTimer)
|
|
643
644
|
return;
|
|
644
645
|
resultDrainTimer = setTimeout(() => {
|
|
645
646
|
console.warn(`[claude-engine] SDK generator still open ${RESULT_DRAIN_TIMEOUT_MS}ms after 'result' — forcing session:ended`);
|
|
@@ -725,6 +726,8 @@ export function createClaudeCodeEngine() {
|
|
|
725
726
|
// Human waits suspend it independently; genuine activity restarts it.
|
|
726
727
|
const armSubagentStallWatchdog = () => {
|
|
727
728
|
clearSubagentStallWatchdog();
|
|
729
|
+
if (!options.watchdogEnabled)
|
|
730
|
+
return;
|
|
728
731
|
if (pendingResolvers.size > 0 || activeSubagentTaskIds.size === 0)
|
|
729
732
|
return;
|
|
730
733
|
const hasAgent = [...activeSubagentTaskIds].some((id) => isSubagentTask({ taskType: mapperState.taskTypes.get(id) }));
|
|
@@ -205,6 +205,8 @@ export function createCodexEngine() {
|
|
|
205
205
|
// deadline tracks the last activity, not the start of the wait.
|
|
206
206
|
const armSubagentStallWatchdog = () => {
|
|
207
207
|
clearSubagentStallWatchdog();
|
|
208
|
+
if (!options.watchdogEnabled)
|
|
209
|
+
return;
|
|
208
210
|
if (!waitingForBackgroundSubagents || pendingByCallId.size > 0 || activeSubagentThreads.size === 0)
|
|
209
211
|
return;
|
|
210
212
|
subagentStallTimer = setTimeout(() => {
|
|
@@ -251,6 +253,7 @@ export function createCodexEngine() {
|
|
|
251
253
|
}
|
|
252
254
|
};
|
|
253
255
|
const turnLiveness = createTurnLiveness({
|
|
256
|
+
enabled: options.watchdogEnabled === true,
|
|
254
257
|
timeoutMs: CODEX_TURN_IDLE_TIMEOUT_MS,
|
|
255
258
|
onTimeout() {
|
|
256
259
|
watchdogEnded = true;
|
|
@@ -496,6 +496,7 @@ function readEffectiveSettingsSafe(projectPath) {
|
|
|
496
496
|
function buildMcpServers(workspaceId, extraEnv, memoryCapability) {
|
|
497
497
|
const mcpServerCompiled = getCompiledMcpServerPath();
|
|
498
498
|
const mcpServerSource = getMcpServerSourcePath();
|
|
499
|
+
const global = getGlobalSettings();
|
|
499
500
|
return [
|
|
500
501
|
{
|
|
501
502
|
name: 'kobo-tasks',
|
|
@@ -508,6 +509,11 @@ function buildMcpServers(workspaceId, extraEnv, memoryCapability) {
|
|
|
508
509
|
KOBO_DB_PATH: getDbPath(),
|
|
509
510
|
KOBO_SETTINGS_PATH: getSettingsPath(),
|
|
510
511
|
KOBO_BACKEND_URL: `http://127.0.0.1:${backendPort}`,
|
|
512
|
+
// Behind a proxy, even internal loopback requests require network auth.
|
|
513
|
+
// Per-launch memory/progress capabilities remain independently required.
|
|
514
|
+
...(global.networkAccessBehindProxy && global.networkAccessToken
|
|
515
|
+
? { KOBO_NETWORK_TOKEN: global.networkAccessToken }
|
|
516
|
+
: {}),
|
|
511
517
|
},
|
|
512
518
|
},
|
|
513
519
|
];
|
|
@@ -1571,6 +1577,7 @@ export function startAgent(workspaceId, workingDir, prompt, model, resume = fals
|
|
|
1571
1577
|
const integrationServers = buildIntegrationMcpServers(getGlobalSettings(), engine.id, (name) => unavailableIntegrations.push(name));
|
|
1572
1578
|
const options = {
|
|
1573
1579
|
workspaceId,
|
|
1580
|
+
watchdogEnabled: getGlobalSettings().agentWatchdogEnabled === true,
|
|
1574
1581
|
workingDir,
|
|
1575
1582
|
prompt: buildAgentPrompt(`${ws ? renderWorkflowPolicy(ws.workflowPolicy) : ''}\n${integrationMcpPrompt(integrationServers, unavailableIntegrations)}\n${prompt}`, ws?.projectPath),
|
|
1576
1583
|
model,
|
|
@@ -13,7 +13,7 @@ export function createTurnLiveness(input) {
|
|
|
13
13
|
let timedOut = false;
|
|
14
14
|
let currentTimeoutMs = input.timeoutMs;
|
|
15
15
|
const arm = () => {
|
|
16
|
-
if (!active || paused || timedOut)
|
|
16
|
+
if (input.enabled === false || !active || paused || timedOut)
|
|
17
17
|
return;
|
|
18
18
|
if (timer)
|
|
19
19
|
clearTimeout(timer);
|
|
@@ -980,6 +980,22 @@ const settingsMigrations = [
|
|
|
980
980
|
global.memoryMode = 'hybrid';
|
|
981
981
|
},
|
|
982
982
|
},
|
|
983
|
+
{
|
|
984
|
+
version: 64,
|
|
985
|
+
name: 'opt-in-agent-watchdogs',
|
|
986
|
+
migrate: ({ global }) => {
|
|
987
|
+
if (typeof global.agentWatchdogEnabled !== 'boolean')
|
|
988
|
+
global.agentWatchdogEnabled = false;
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
version: 65,
|
|
993
|
+
name: 'session-progress-visibility',
|
|
994
|
+
migrate: ({ global }) => {
|
|
995
|
+
if (typeof global.showProgressBars !== 'boolean')
|
|
996
|
+
global.showProgressBars = true;
|
|
997
|
+
},
|
|
998
|
+
},
|
|
983
999
|
];
|
|
984
1000
|
/** Current settings schema version — always equals the highest migration version. */
|
|
985
1001
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -1035,6 +1051,8 @@ function defaultSettings() {
|
|
|
1035
1051
|
terminalCommand: '',
|
|
1036
1052
|
autoPurgeOnPrMerged: false,
|
|
1037
1053
|
autoLoopMaxRetries: 5,
|
|
1054
|
+
agentWatchdogEnabled: false,
|
|
1055
|
+
showProgressBars: true,
|
|
1038
1056
|
awaitingUserReminderMinutes: 0,
|
|
1039
1057
|
activityDigestEnabled: true,
|
|
1040
1058
|
maxConcurrentAgents: 2,
|
|
@@ -1183,6 +1201,10 @@ export function runSettingsMigrations(raw) {
|
|
|
1183
1201
|
current.global.workflowPolicy = resolveWorkflowPolicy(current.global.workflowPolicy);
|
|
1184
1202
|
if (!isMemoryMode(current.global.memoryMode))
|
|
1185
1203
|
current.global.memoryMode = 'hybrid';
|
|
1204
|
+
if (typeof current.global.showProgressBars !== 'boolean')
|
|
1205
|
+
current.global.showProgressBars = true;
|
|
1206
|
+
if (typeof current.global.agentWatchdogEnabled !== 'boolean')
|
|
1207
|
+
current.global.agentWatchdogEnabled = false;
|
|
1186
1208
|
for (const project of current.projects) {
|
|
1187
1209
|
if (!isWorkflowPolicy(project.workflowPolicy))
|
|
1188
1210
|
project.workflowPolicy = {};
|
|
@@ -1535,6 +1557,12 @@ export function updateGlobalSettings(input) {
|
|
|
1535
1557
|
delete data.autoLoopMaxRetries;
|
|
1536
1558
|
}
|
|
1537
1559
|
}
|
|
1560
|
+
if ('showProgressBars' in data && typeof data.showProgressBars !== 'boolean') {
|
|
1561
|
+
delete data.showProgressBars;
|
|
1562
|
+
}
|
|
1563
|
+
if ('agentWatchdogEnabled' in data && typeof data.agentWatchdogEnabled !== 'boolean') {
|
|
1564
|
+
delete data.agentWatchdogEnabled;
|
|
1565
|
+
}
|
|
1538
1566
|
// 0 is a legitimate value here — it means "never remind me" — so the floor is
|
|
1539
1567
|
// 0, not 1. The ceiling of a day keeps a typo from arming a reminder that
|
|
1540
1568
|
// never fires and looks like a broken feature.
|
|
@@ -1598,6 +1626,8 @@ export function updateGlobalSettings(input) {
|
|
|
1598
1626
|
'terminalCommand',
|
|
1599
1627
|
'autoPurgeOnPrMerged',
|
|
1600
1628
|
'autoLoopMaxRetries',
|
|
1629
|
+
'agentWatchdogEnabled',
|
|
1630
|
+
'showProgressBars',
|
|
1601
1631
|
'awaitingUserReminderMinutes',
|
|
1602
1632
|
'activityDigestEnabled',
|
|
1603
1633
|
'maxConcurrentAgents',
|
package/docker-compose.vps.yml
CHANGED
|
@@ -10,10 +10,16 @@ services:
|
|
|
10
10
|
init: true
|
|
11
11
|
restart: unless-stopped
|
|
12
12
|
# Use the host's SSH and `docker compose exec`; no container SSH daemon.
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
# Bootstrap permissions as root, then execute Kōbō/agents as this host UID/GID.
|
|
14
|
+
entrypoint: ["kobo-vps-entrypoint"]
|
|
15
|
+
command: ["node", "dist/server/index.js"]
|
|
15
16
|
environment:
|
|
16
|
-
|
|
17
|
+
KOBO_UID: ${KOBO_UID:-1000}
|
|
18
|
+
KOBO_GID: ${KOBO_GID:-1000}
|
|
19
|
+
HOME: /root
|
|
20
|
+
# Kōbō serves its built assets; project commands need development dependencies.
|
|
21
|
+
NODE_ENV: development
|
|
22
|
+
NPM_CONFIG_INCLUDE: dev
|
|
17
23
|
SERVER_PORT: "3000"
|
|
18
24
|
KOBO_HOME: /root/.config/kobo
|
|
19
25
|
KOBO_NETWORK_ACCESS_ENABLED: "true"
|