daemon-manual · diff
v0.13.2 to v0.15.0
163 added, 577 removed. Audit A to A.
---
name: daemon-manual
description: >
Read before delegating work, diagnosing a slow/stuck/failed/timed-out
- emanation (including exit 143 / SIGTERM), or before reclaiming on a hunch;
- owns CLI backends/`backend_options`, settings meaning, polling cadence,
- compact, and footprint cleanup procedures.
- version: 0.13.2
- last_changed_at: 2026-09-04T00:00:00Z
+ emanation, or reclaiming on a hunch. Routes daemon call shape, task context,
+ settings, backend support, inspection, compaction, and cleanup procedures.
+ version: 0.15.0
+ last_changed_at: 2026-09-06T00:00:00Z
related_files:
- src/lingtai/tools/daemon/CONTRACT.md
- src/lingtai/tools/daemon/ANATOMY.md
- src/lingtai/tools/daemon/system_prompt.py
- src/lingtai/tools/daemon/settings.py
- src/lingtai/tools/daemon/execution_host.py
- src/lingtai/tools/daemon/shell_prompt_events.py
- - src/lingtai/tools/bash/manual/SKILL.md
- src/lingtai/tools/daemon/manual/reference/forensics/SKILL.md
+ - src/lingtai/tools/daemon/manual/reference/inspection/SKILL.md
+ - src/lingtai/tools/daemon/manual/reference/cli-backends/SKILL.md
+ - src/lingtai/tools/daemon/manual/reference/cleanup/SKILL.md
- src/lingtai/tools/daemon/manual/reference/dispatch-ledger/SKILL.md
+ - src/lingtai/tools/bash/manual/SKILL.md
- tests/test_daemon_settings.py
maintenance: |
- Tracks the routed source/resources it summarizes; update when the underlying capability or its sub-references change.
+ Keep this as a short router. Put backend recipes, artifact forensics,
+ inspection cadence, ledger diagnosis, and cleanup detail in the nested
+ references; keep links and settings anchors stable.
---
# Daemon Manual — Router
- The `daemon` tool schema covers dispatch/follow-up/check/reclaim/settings. This manual
- routes to deeper operational references: how to inspect daemon artifacts, decide
- whether work is stuck, use CLI backends safely, and clean up old emanations.
-
- Scope note: this manual does **not** restate the daemon tool argument schema, and
- it does not document cross-process recovery/orphan-detection internals. For the
- broader runtime turn loop that daemon emanations mirror, use `lingtai-kernel-anatomy`
- and its runtime-loop reference.
-
- Maintainer note: the unified daemon contract is
- `src/lingtai/tools/daemon/CONTRACT.md`. Update or explicitly re-check that
- contract when changing backend routing, selected `skills`, one-run `mcp`, native
- MCP mounting, `daemon_common`, backend support status, or run artifacts.
-
- Use the smallest reference that matches the problem. Do not kill or reclaim a
- daemon on a hunch; inspect first.
-
- ## Nested reference catalog
-
- `daemon-manual` owns these nested references. They are parent-owned drill-down
- files, not standalone top-level skills.
-
- ```yaml
- - name: daemon-forensics
- location: reference/forensics/SKILL.md
- description: |
- Daemon artifact forensics: persistent daemons/em-* folders, daemon.json
- status fields, chat_history.jsonl, token_ledger.jsonl, events.jsonl,
- interpreting exit code 143 / SIGTERM (terminated, not a test/code failure),
- and how to inspect progress without guessing.
- - name: daemon-inspection
- location: reference/inspection/SKILL.md
- description: |
- Polling cadence, stall heuristics, anti-patterns, backend-specific polling
- notes, and reminders before resting while daemon work remains pending.
- - name: daemon-cli-backends
- location: reference/cli-backends/SKILL.md
- description: |
- Daemon API details and CLI backends: daemon(action=list, input={}), claude-p/codex/opencode behavior,
- backend_options flag passing, preset/capability inheritance, and Codex
- modal capabilities.
- - name: daemon-cleanup
- location: reference/cleanup/SKILL.md
- description: |
- Scope boundaries and daemon footprint cleanup: what the manual does not
- cover, reclaim persistence, and safe cleanup of old daemon artifacts.
- - name: daemon-dispatch-ledger
- location: reference/dispatch-ledger/SKILL.md
- description: |
- Append-only dispatch membership/order, scoped list warnings, marker-only
- recovery, recent background session snapshots, and no-repair diagnosis.
- ```
-
- ## Call shape
+ `daemon` dispatches disposable subagents (emanations) with isolated sessions and
+ bounded tools. Read this manual before the first call. Put the complete
+ objective, authority, safety boundary, collaboration rules, and deliverable in
+ each task's `task`; `tools` grants capability only. The daemon is not a durable
+ persona or hidden memory store: leave reviewable work in files and use run
+ artifacts for follow-up.
- Every `daemon` call is the same four-field envelope: a required `action`, a
- required `input` object holding **only** that action's own fields, and a
- required `reasoning` string. The optional root `summarize` boolean replaces the
- former flat `summary` field. Passing another action's field — or any field at
- the call root — is refused before the daemon engine runs.
+ ## Routing table
- | Action | Call |
+ | Need / keywords | Read |
|---|---|
- | `emanate` | `daemon(action="emanate", input={"tasks": [{"task": "...", "tools": ["file"]}], "backend": "codex"}, reasoning="...")` |
- | `list` | `daemon(action="list", input={"status": "running", "last": 20}, reasoning="...")` |
- | `ask` | `daemon(action="ask", input={"id": "em-1", "message": "..."}, reasoning="...")` |
- | `check` | `daemon(action="check", input={"id": "em-1", "last": 20, "truncate": 500}, reasoning="...")` |
- | `reclaim` | `daemon(action="reclaim", input={}, reasoning="...")` |
- | `settings` | `daemon(action="settings", input={}, reasoning="...")` |
- | `manual` | `daemon(action="manual", input={}, reasoning="...")` |
-
- `list`, `check`, `settings`, and `manual` are read-only. `emanate`, `ask`, and `reclaim`
- are the three that change state.
-
- ## Settings inventory
-
- `daemon(action="settings", input={}, reasoning="inspect daemon settings")`
- is SHOW-only. Success contains only `{"settings": [...]}`; every row has exactly
- `key`, `current`, `default`, `configurable`, and the exact manual-section
- pointer `comment`. The action accepts no set/reset or other input and never
- writes a config file, process environment, launcher state, or daemon run.
-
- `configurable: true` means an authorized owner can use the existing procedure
- in the named section and then verify the new active value with a second SHOW.
- It does not authorize the caller or this action to perform the change.
-
- ### Max turns
-
- `max_turns` is the active manager's default and ceiling for a new run's
- tool-loop turns; its meaningful default is `5000`. Accepted owner values are
- positive integers. Source precedence is a valid positive-integer
- `LINGTAI_DAEMON_MAX_TURNS` environment value, an explicit daemon capability or
- setup value, a positive `<workdir>/daemon/daemon.json` key `max_turns`, then
- the default. Invalid environment input retains the valid explicit, file, or
- default result. The per-call `emanate.max_turns` input may select a smaller
- value for one batch but does not change this owner setting.
-
- An authorized owner changes the launcher/environment,
- `manifest.capabilities.daemon.max_turns` in `init.json`, or the existing
- `daemon/daemon.json` key with the normal File/Shell procedure, then refreshes
- or relaunches the agent. The value applies when a new `DaemonManager` is built;
- SHOW never writes it.
-
- ### Manager pool size
-
- `manager_pool_size` bounds concurrent POSIX central-manager execution
- children; `0` selects the classic per-run supervisor path. Its default is
- `100`, and accepted values are non-negative integers. Source precedence is a
- valid `LINGTAI_DAEMON_MANAGER_POOL_SIZE` environment value, an explicit daemon
- capability or setup value, a valid `daemon/daemon.json` key
- `manager_pool_size`, then the default. Invalid environment or file input falls
- through without repair.
-
- An authorized owner updates the launcher/environment,
- `manifest.capabilities.daemon.manager_pool_size`, or the existing owner file
- with the normal Shell/File/config procedure, then refreshes or relaunches. The
- value applies when the manager is rebuilt; SHOW only reports the active
- snapshot.
-
- ### System prompt budget chars
-
- `system_prompt_budget_chars` is the character limit for a LingTai daemon's
- rendered system prompt. Over-budget prompts fail rather than truncate
- constraints. The default is `20000`; accepted values are positive integers.
- Source precedence is a valid
- `LINGTAI_DAEMON_SYSTEM_PROMPT_BUDGET_CHARS` environment value, an explicit
- daemon capability or setup value, a valid `daemon/daemon.json` key
- `system_prompt_budget_chars`, then the default. Invalid input falls through.
-
- An authorized owner uses the existing launcher/environment,
- `manifest.capabilities.daemon.system_prompt_budget_chars`, or owner-file
- procedure and then refreshes or relaunches. The value applies to a newly built
- manager; SHOW performs no write.
+ | First use, safe call shape, task authority, settings, completion, or compact | This router, then the relevant section below |
+ | `backend="lingtai"`; built-in/in-process backend; preset, skills, MCP, or `finish` behavior | `reference/cli-backends/reference/backends/lingtai/SKILL.md` directly; no generic CLI page is required |
+ | External CLI backend, alias, resume, `backend_options`, native MCP/config, or installed `--help` | `reference/cli-backends/SKILL.md`, then its per-backend page |
+ | `list`/`check` cadence, stall, reminder, or intervention | `reference/inspection/SKILL.md` |
+ | Run artifacts, events, transcripts, token records, or SIGTERM/143 | `reference/forensics/SKILL.md` |
+ | Dispatch-ledger warnings or runtime identity mismatch | `reference/dispatch-ledger/SKILL.md` |
+ | Reclaim, footprint, consent-gated cleanup, or deletion boundary | `reference/cleanup/SKILL.md` |
+ | Shell async events or shell-side supervision | `shell-manual` |
+ | Programmatic use, CLI/help, or scripts/CI | `lingtai-agent daemon --help` |
- ### Timeout
+ The built-in route is intentionally separate from the generic CLI route. The
+ external page remains the owner of detailed CLI support, alias/resume behavior,
+ MCP/config injection, reserved flags, auth hygiene, and per-backend discovery.
- `timeout` is the active manager's default wall-clock seconds for a run and for
- supported follow-up workers; its default is `3600.0`. The operationally valid
- owner value is a finite JSON number (integer or float, but not a Boolean) of at
- least `5` seconds; there is no upper bound. Its only source is the
- launcher/capability setup layer: the owner value has no daemon-file or
- environment peer. An explicit
- `manifest.capabilities.daemon.timeout` value is passed by capability setup,
- otherwise the default applies. A one-run `emanate.timeout` input does not
- change the owner setting.
+ ## Call shape and action choice
- The existing setup seam does not enforce that type, range, or finiteness: the
- `float` annotation is informational, and `setup()` / `DaemonManager` store an
- explicit value unchanged. A JSON-finite wrong-type or out-of-range value is
- therefore neither coerced nor replaced by the default; SHOW reports that exact
- active value. If a non-finite float reaches setup, it is likewise stored, but
- the generic settings serializer refuses non-finite JSON, so SHOW fails the
- whole inventory with `SETTINGS_UNAVAILABLE` and emits no partial rows. Invalid
- stored values can fail later when timeout arithmetic or persistence consumes
- them; there is no automatic repair or fallback.
+ Every call uses the closed LTP-v2 envelope. `input` contains only the selected
+ action's fields; `reasoning` is required; root `summarize` is optional and is
+ not action input. The optional root `summarize` boolean replaces the former flat `summary` field. An action's other fields are rejected before the engine runs.
- An authorized owner changes the daemon capability value in `init.json` using
- the normal config/File procedure and refreshes or relaunches. The new default
- applies when the manager is rebuilt; SHOW never mutates it.
+ | Action | Example | Effect |
+ |---|---|---|
+ | `emanate` | `daemon(action="emanate", input={"tasks": [{"task": "...", "tools": ["file"]}]}, reasoning="...")` | dispatches |
+ | `list` | `daemon(action="list", input={"status": "running"}, reasoning="...")` | read-only |
+ | `ask` | `daemon(action="ask", input={"id": "run-id", "message": "..."}, reasoning="...")` | follow-up |
+ | `check` | `daemon(action="check", input={"id": "run-id"}, reasoning="...")` | read-only |
+ | `settings` | `daemon(action="settings", input={}, reasoning="inspect daemon settings")` | read-only |
+ | `reclaim` | `daemon(action="reclaim", input={}, reasoning="stop confirmed work")` | cancels |
+ | `manual` | `daemon(action="manual", input={}, reasoning="read procedures")` | read-only |
- ## Programmatic use / CLI
+ `manual` is directly callable and reaches no daemon engine. An `emanate` returns
+ ids and a batch `group_id`; use each id/run id for inspection and audit. `list`, `check`, `settings`, and `manual` are read-only; `emanate`, `ask`, and `reclaim`
+ are side-effectful. The terminal distinctions are fixed and cannot be inferred
+ from a field borrowed from another action.
- Callers outside a live agent turn — a shell script, a Python subprocess, a CI
- job — should use the `lingtai-agent daemon` subcommand instead of scripting this
- tool. It runs the same engine (`DaemonManager`) through the same action
- envelope, so dispatch, run directories, presets, and result shapes are
- identical.
+ ## First dispatch: task context and boundaries
- ```bash
- lingtai-agent daemon emanate --tasks batch.json --agent-dir ~/agents/foo [--backend lingtai] [--yes]
- lingtai-agent daemon list [--status running] [--last N] [--agent-dir ~/agents/foo]
- lingtai-agent daemon check em-1 [--agent-dir ~/agents/foo]
- ```
+ Each `tasks[]` item requires `task` and `tools`.
- `--tasks` takes the tool's own `emanate` input object (`tasks`, and optionally
- `backend` / `max_turns` / `timeout`), or a bare array of task objects:
+ - **`task`** is the complete parent-controlled objective, authority, constraints,
+ safety posture, collaboration rules, tool-use policy, and deliverable. The
+ removed `system_prompt` field has no alias; put its instruction here.
+ - **`tools`** grants technical capability, not permission beyond `task`. Parent
+ MCP tools are not inherited. Guarded calls still pass the normal executor and
+ call-guard path.
+ - **`skills`** is an optional list of skill directories or `SKILL.md` paths.
+ Relative paths use the parent working directory; runtime injects a compact
+ frontmatter catalog and the worker reads selected skills when relevant.
+ - **`mcp`** is an optional list of complete one-run stdio/http registrations.
+ Names and shape remain visible while secret `env`/`headers` are redacted.
+ LingTai mounts task-scoped clients; exposed tool names must be unique. External
+ CLIs mount only transports documented in the backend reference.
+ - **`plugin`** is an optional plugin directory or search-root list. A manifest
+ becomes compact prompt context and merges its skills/MCP; missing or unreadable
+ paths resolve to nothing.
+ - **`task_files`** is an optional `{path, label?, role?}` UTF-8 text list under
+ the parent working directory. Dispatch validates containment, encoding, and
+ size, then snapshots bytes into the immutable store; the worker gets metadata
+ and snapshot paths, never mutable originals or contents. A bad entry rejects
+ the whole batch.
+ - **`preset`** is an optional authorized `.json`/`.jsonc` path from
+ `system(action="presets")`; use the full returned path. Omission inherits the
+ parent's regular tools; MCP still needs explicit task registrations. An
+ unauthorized preset is refused before loading or scheduling.
+ - **`prompt`** is LingTai-only and is the first ordinary user message. Blank or
+ omitted means exactly `Begin the assigned daemon task.`; external CLIs reject
+ it and use `task` as their prompt.
+ - **`backend_options`** is CLI-only passthrough: booleans emit flags, scalars
+ values, lists repeat flags, false/null omit them, and reserved `env` supplies
+ string environment overrides. Unsafe or harness-owned keys fail preflight;
+ options apply at `emanate`, not `ask`. Run the installed CLI's `--help` first.
- ```json
- {"tasks": [{"task": "Summarize docs/ into notes.md", "tools": ["file"]}]}
- ```
+ ### Per-task `context_token_limit`
- Behavior worth knowing before wiring it into a job:
+ This is a provider-compaction threshold, not a daemon context or window setting.
+ For native-provider support, resolved-window ownership, and failure behavior, use
+ the built-in LingTai child route in the table above. External CLI backends ignore
+ this field.
- - **`emanate` previews by default.** Without `--yes` it prints the batch it
- *would* dispatch (count, backend, presets, per-task tools) and exits 0
- without spawning anything. With `--yes` it prints the tool's own emanate
- result — `status`, `count`, `ids`, `group_id`, `handoff`.
- - **The preview is fully validated.** The tasks file is checked against this
- tool's own `emanate` schema before `--yes` is even considered: backend enum,
- field types, `max_turns` 1..5000, `timeout` ≥ 5, `context_token_limit` ≥ 1.
- An invalid file exits non-zero with every violation listed, rather than
- printing a preview that dispatch would reject.
- - **`--agent-dir` is required for `emanate`** and must contain `init.json`;
- `list` / `check` default to the current directory.
- - **`emanate` reads the agent's *effective* configuration**, through the same
- canonical reader boot uses: JSONC parsed, active preset materialized,
- `provider: "inherit"` expanded, schema validated, and every relative path
- (notably `env_file`) resolved against `--agent-dir` rather than the caller's
- working directory. A daemon therefore launches on the preset's effective
- provider/model, not the raw `manifest.llm` the preset replaces. An
- unusable `init.json` refuses the batch.
- - **Capability policy is enforced.** A task may only request tools this agent
- actually grants — `manifest.capabilities` overlaid on the core floor, minus
- `manifest.disable` — and each is instantiated with the agent's authored
- kwargs. Requesting a disabled tool refuses the *whole* batch. Tasks naming an
- explicit `preset` are governed by that preset's own sandbox instead.
- - **Preset allowlist is fail-closed.** A task naming a preset outside
- `manifest.preset.allowed` refuses the *whole* batch, at preview time as well
- as at dispatch. An agent with no allowlist grants no preset.
- - **`list` and `check` are categorically read-only.** Default list tails the
- newest 1000 append-order dispatch records and reads only their referenced
- `daemon.json` files; it never scans, sorts, backfills, or repairs historical
- folders. Its scoped `warnings` are advisory. A known legacy run remains
- inspectable by exact id/filesystem forensics, not by an automatic migration.
- Pass a positive `last: N` for an explicit page; non-empty `contains` searches prompt-preview text
- among explicitly ledger-selected candidates and may stream more ledger records as an ad-hoc query.
- - **Terminal notifications stay with the owning agent.** Runs dispatched from
- the CLI are detached and still notify the agent that owns the working
- directory; the CLI process itself publishes nothing.
- - **Secrets are redacted from CLI output** (`backend_options.env`, MCP
- `env`/`headers`, credential-shaped keys), using the same policy as the
- durable daemon manifest.
- - `ask` and `reclaim` are deliberately not exposed: this surface causes no side
- effect beyond spawning daemons.
+ ## Backend choice and support
- ## Router table
+ `lingtai` is the default in-process backend. `claude-p` (alias `claude-code`),
+ Codex, OpenCode, Qwen Code, and Kimi Code have source-backed native
+ `daemon_common` checkpoint/completion paths as documented in the CLI reference.
+ MiMo Code, Oh-My-Pi, Cursor, and DeepSeek retain prompt-catalog-only
+ MCP/completion status; prompt text does not grant native tools. Hidden
+ interactive Claude is for legacy stored runs, not new selection. Do not invent
+ a fallback for an unsupported native path.
- | Need / keywords | Read |
- |---|---|
- | Find an emanation's folder; inspect `daemon.json`, transcript, token ledger, event log; understand result paths or token attribution | `reference/forensics/SKILL.md` |
- | Interpret a CLI-backend **exit code 143 / SIGTERM** (terminated from outside — watchdog/timeout/reclaim — not a test or code failure); decide rerun vs hand-off; report it to a human | `reference/forensics/SKILL.md` |
- | Decide whether a daemon is stuck; choose when to list/check/tail; avoid polling too often; set a reminder before resting | `reference/inspection/SKILL.md` |
- | Use `daemon(action="list", input={})`; choose `lingtai` vs `claude-p`/`codex`/`opencode`; pass `backend_options`; understand CLI backend limitations | `reference/cli-backends/SKILL.md` |
- | Retire or audit old daemon artifacts; understand what `reclaim` does and does not delete; scope boundaries | `reference/cleanup/SKILL.md` |
- | Interpret `daemon(list)` ledger warnings, missing legacy history, malformed tails, recovery markers, or recent snapshot lag | `reference/dispatch-ledger/SKILL.md` |
+ Before relying on an external CLI, inspect its installed `--help`. `tools` in
+ the task is ignored by external CLIs; `prompt` is LingTai-only. CLI backend
+ support, aliases, resume limits, reserved options, auth, and native MCP
+ boundaries belong to `reference/cli-backends/SKILL.md` and its child pages.
- ## Quick decision tree
+ ## Settings inventory (SHOW only)
- 1. **Need only the daemon tool argument schema?** Use the tool description.
- 2. **Daemon seems slow?** Read `reference/forensics/SKILL.md`, then
- `reference/inspection/SKILL.md` if you might intervene.
- 3. **Daemon failed/timed out?** Read recent events/transcript via the forensics
- reference before retrying.
- 4. **Choosing an execution backend or flags?** Read
- `reference/cli-backends/SKILL.md`.
- 5. **Cleaning old folders?** Read `reference/cleanup/SKILL.md` and avoid deleting
- useful forensic evidence without a reason.
+ Call `daemon(action="settings", input={}, reasoning="inspect daemon settings")`.
+ Success is exactly `{"settings": [...]}` with row fields `key`, `current`,
+ `default`, `configurable`, and `comment`. This action has no set/reset input and
+ writes no files, environment, launcher state, or run. `configurable: true` means
+ an owner procedure exists; it grants no mutation authority. A serialization/read
+ failure returns `SETTINGS_UNAVAILABLE`, never partial rows.
- ## High-concurrency central manager (Phase 1)
+ ### Max turns
- For high-concurrency daemon batches, the kernel can route work through a
- **central daemon manager** instead of spawning one detached supervisor per run.
- The manager is a single thin POSIX process per agent that owns queue intake,
- assignment, deadline/control, terminal truth, idempotent notification, and a
- durable journal with restart recovery. Its purpose is to cap RAM growth: at most
- `manager_pool_size` execution children run concurrently, and runs beyond that
- queue as pure state (≈0 MB RAM) until a worker frees.
+ Anchor: `daemon-manual#max-turns`. Current value and application belong to
+ `daemon.settings`; precedence and setup details belong to the Contract. Per-call
+ `emanate.max_turns` may choose a smaller positive value for one batch.
- Configuration remains per-agent and is not written by SHOW. See
- [Manager pool size](#manager-pool-size) for its accepted values, precedence,
- apply timing, and existing change procedure.
+ ### Manager pool size
- Behavior notes:
- - The manager path is POSIX-only and enabled by default (`manager_pool_size`
- default `100`); Windows and an explicit `manager_pool_size: 0` keep the
- classic per-run supervisor behavior.
- - When enabled, every batch size runs through the manager and at most
- `manager_pool_size` execution children run at a time; the rest queue and
- execute as workers free.
- - Queued runs keep only secret-free durable state under `<agent>/daemon/manager`.
- Their runtime capsules are memory-only. If the manager restarts before assigning
- a queued run, that run is failed with `manager_restart_capsule_unavailable`
- evidence and terminal notification instead of being replayed.
- - Phase 1 still spawns one execution child per active run (no reusable LLM
- worker yet); the reusable worker pool is a later phase.
- - **For large concurrent batches, strongly recommend delaying the `daemon`
- notification channel** with `notification(action="delay", input={"channel":
- "daemon", "seconds": <duration>})` so terminal arrivals do not repeatedly
- wake the parent. Delay masks attention only: per-run mini-files, durable
- receipts, `daemon.json`, and aggregate truth remain readable; choose a bounded
- duration and let the delay-alarm mirror re-expose the channel. Read
- `notification-manual` before selecting the duration or handling expiry.
+ Anchor: `daemon-manual#manager-pool-size`. Current value and application belong
+ to `daemon.settings`; precedence and rebuild details belong to the Contract.
- ### Runtime-identity mismatch after refresh
+ ### System prompt budget chars
- If `emanate` refuses before a new worker starts with either **“resident central
- daemon manager runtime identity does not match”** or **“starting central daemon
- manager runtime identity does not match”**, stop retrying and use this manual.
- This is a fail-closed safety fence: it prevents a persistent POSIX manager loaded
- from different code, source root, or daemon-notification protocol from accepting
- new work. Existing queued or active work is deliberately not taken over.
+ Anchor: `daemon-manual#system-prompt-budget-chars`. Current value and
+ application belong to `daemon.settings`; prompt-budget behavior belongs to the
+ Contract. SHOW does not mutate it.
- A common cause is an agent that has refreshed or changed runtime source while an
- older resident manager process remains alive (including a legacy record without
- the runtime-identity stamp). It is not by itself evidence that the requested
- model, preset, task, or repository is broken.
+ ### Timeout
- 1. **Inspect; do not reclaim or kill on a hunch.** Give the runtime owner the
- error, manager PID/incarnation, runtime-identity record, queue count, and
- journal states. A mismatch can coexist with real queued or active work.
- 2. **Preserve fail-closed safety.** If the PID/incarnation cannot be confirmed,
- any queue entry exists, or any journal is nonterminal, do not terminate or
- take over the manager. Escalate to the runtime owner.
- 3. **Only an authorized owner may recover an actually idle manager.** After
- confirming the exact manager process, an empty queue, and terminal-only
- journal records, perform a controlled manager restart, then run one small
- no-write daemon check. Refresh/relaunch the agent only through its normal
- lifecycle procedure; do not edit private manager records to bypass the fence.
- 4. **Report the outcome.** State whether a fresh manager wrote a current runtime
- identity and whether the bounded check dispatched and finished. If it still
- refuses, preserve the exact error and escalate rather than repeatedly retrying.
+ Anchor: `daemon-manual#timeout`. Current value and application belong to
+ `daemon.settings`; precedence and failure behavior belong to the Contract.
+ Per-call `emanate.timeout` is a one-batch override, not a settings mutation.
- ## Core rules to keep resident
+ ## Progress, inspection, and follow-up
- - Keep daemon lightweight. If the task needs long-lived persona, molt/pad,
- durable knowledge, or ongoing ownership, spawn an avatar/agent instead of
- stretching daemon.
- - **Do not set a small `max_turns` merely because a task looks simple.** Omit it
- to use the default, or set it only when a concrete operational boundary still
- leaves enough turns for unexpected investigation, validation, and a truthful finish.
- - Every LingTai daemon receives a short package-owned operating prompt before
- the parent task. It tells the worker to read a relevant manual before first
- using a tool/workflow that has one, use a visible result tool's `summary=true`
- only for predictable bulky output whose exact raw text is unnecessary, use
- daemon `compact` rather than the unavailable parent `system.summarize`, and
- finish truthfully. The complete rendered system prompt is capped at 20,000
- characters by default and fails instead of truncating task/skill/MCP
- context — see "System prompt budget chars" above for the exact
- override precedence. Keep the run-specific objective,
- authority, safety boundary, and deliverable in `task`.
- - On the LingTai backend, explicit `summary=true` uses a daemon-local,
- no-tools session on the same effective service, provider, and model. It keeps
- raw output in the run-local `logs/events.jsonl` and returns the generated
- summary (or fail-closed error) with the exact recovery locator.
- - Think of each task item as **task objective + behavior guidance + tool
- surface**:
- - `task` is the complete parent-controlled daemon system instruction:
- objective, role, constraints, safety posture, collaboration boundaries, and
- tool-use policy. The removed `system_prompt` field has no alias; migrate its
- complete contents into `task`.
- - `prompt` is optional and LingTai-only. It is the first ordinary user message,
- sent exactly when nonblank, or exactly `Begin the assigned daemon task.` when omitted/blank.
- External CLI backends reject it and keep `task` as their CLI prompt.
- - `tools` answers **what the daemon can technically use** for this run. The
- parent puts the complete operating contract in `task`: when and how those
- tools should be used, for example read-only file access, no network, write
- only to one report path, or ask a named peer before guessing. `email` is
- daemon-eligible communication, but it is not granted by default; include
- `tools: ["email"]` only when the daemon should be able to use internal mail.
- Other tool names still matter for file/shell/web/etc. access.
- - `skills` answers **which workflows the daemon should know about**. It is an
- optional list of strings. Each string may be either a skill directory
- containing `SKILL.md` or a direct `SKILL.md` path; relative paths resolve
- against the parent agent working directory. The runtime parses each skill's
- frontmatter and injects a compact YAML skill list into the daemon prompt.
- Use `task` to say when/how those selected skills should be applied.
- - `mcp` answers **which one-run MCP registrations belong to this daemon**. It
- is optional and is an array of full MCP registration objects: `name` plus
- `transport`/`type` (`stdio` or `http`), then `command`/`args`/`env` for
- stdio or `url`/`headers` for HTTP. The runtime serializes these registrations
- as YAML into every backend's oneshot context. The built-in LingTai backend
- also starts them as task-scoped MCP clients and exposes their tools for this
- run. Claude, Codex, OpenCode, Qwen, and Kimi CLI backends additionally receive
- daemon-generated native MCP configuration for the built-in `daemon_common`;
- Claude, Codex, OpenCode, and Qwen also receive native config for
- parent-provided stdio MCP registrations. Kimi receives native config for
- parent-provided stdio and HTTP MCP registrations through its run-private
- `mcp.json`. HTTP MCP registrations remain prompt catalog context for other
- CLI backends until a backend-specific HTTP MCP config path is implemented.
- LingTai automatically adds the built-in `daemon_common` MCP to MCP-capable
- daemon backends. Its strict live-only
- `checkpoint(state, summary, artifacts?, blocker?, request?)` tool records a
- bounded nonterminal snapshot, wakes the parent, and returns any ID-bound
- parent messages exactly once. Use it at useful boundaries, not as chat,
- polling, stdin injection, or preemption. Its
- `finish(status, summary?, reason?, artifacts?)` tool remains the hard
- terminal-success contract: only `finish(status="done")` permits `done`;
- `failed`/`incomplete`, missing finish, or invalid completion prevents
- silent success. A daemon that ends without calling `finish()` is reported
- as a missing-finish failure; that is not necessarily proof the underlying
- task failed — before treating the work as lost, inspect the run's trace/result
- and the full final text preserved in the run directory's
- physical `result.txt` (the run directory is the `path` that `check`
- reports; `result_path` stays null on this failure path). Secret
- `env`/`headers` values are redacted in prompts.
- - `plugin` answers **which Agent Plugins belong to this daemon run**. It is
- optional and is an array of paths; each item may be a plugin directory
- containing `plugin.json`, or a plugin search root whose immediate children
- are plugin directories. Relative paths resolve against the parent agent
- working directory. The runtime reads each plugin manifest and injects a
- compact plugin section (name, summary, skills list, mcp list) into the
- daemon system prompt. The built-in LingTai backend also mounts the plugin's
- `skills/` as skill context and its `mcp.json` servers as task-scoped MCP
- clients, exactly like the main agent mounts plugins. CLI backends that
- cannot mount plugins yet receive the plugin's skills and mcp.json servers
- separately as normal skill/mcp oneshot context until the whole-plugin
- injection matures.
- - `task_files` answers **which local text files this daemon should read**. It
- is optional and is an array of `{path, label?, role?}` objects; `path` is a
- UTF-8 text file under the parent agent working directory (absolute or
- relative; relative paths resolve against the parent working directory). At
- dispatch the parent resolves every path, validates UTF-8 and size limits,
- and snapshots the bytes content-addressed into an immutable read-only input
- store (`daemons/_task_files/` under the parent working directory); the
- daemon receives only a compact `## Parent-provided task files` manifest
- listing label/role/sha256/size and the snapshot path for each file — never
- the file contents and never the mutable original paths, so the run keeps
- working (and can be re-checked or relaunched) even if the original file
- changes or is deleted. Malformed, out-of-root, missing, non-UTF-8, or
- oversize entries refuse the whole batch before any run starts. Up to
- `TASK_FILES_MAX_PER_TASK` files per task, `TASK_FILE_MAX_BYTES` per file.
- - `preset`: optional body/model/tool-shape override for this daemon — an
- explicit `.json`/`.jsonc` path. On the LingTai backend it must already be
- a member of the parent agent's resolved `manifest.preset.allowed` set
- (the same fail-closed normalized path check `system(action="refresh")`
- uses); an unauthorized path is refused before load/connectivity/capability
- checks, run-dir creation, scheduling, or dispatch. Being present in the
- saved/library directory is not by itself authorization — call
- `system(action="presets")` first and pass one of the exact paths it
- returns. Omitting `preset` inherits the parent's regular (non-MCP)
- effective surface instead of a fresh independent default, and does not
- perform this allowlist check at all. External CLI backends skip LingTai
- preset resolution entirely and use their own model/tools/permissions. The
- full preset runtime model is owned by `system-manual` →
- `reference/substrate-manual/SKILL.md` §11.
- - `backend_options`: raw CLI flags for CLI backends only.
- - `context_token_limit`: optional context-token compaction threshold (rendered/provider-context tokens, not cumulative spend). Effective only for `backend="lingtai"` tasks whose resolved provider is Codex (`codex`/`codex-pool`) or the native `mimo` LLM provider (`manifest.llm.provider="mimo"` — NOT the `backend` enum's `mimo`/`mimocode` alias, which drives the external `mimo` CLI as a subprocess and never consults this field at all); every other provider and every external CLI backend ignores it. This threshold does not set the daemon context window. When the session's provider-visible input-token count reaches the limit, the runtime compacts provider context via that provider's standalone compaction (`POST /responses/compact`) and continues the same tool loop — the daemon keeps running; nothing restarts or drops history. Native `mimo` defaults to the stateless OpenAI Responses wire (full-history/raw-output-item replay; never `store`/`previous_response_id`/`conversation`/generic `context_management` — MiMo's Responses API marks those incompatible); an explicit `wire_api="chat_completions"` on the preset still selects the Chat Completions escape hatch instead. **Failure policy differs by provider:** a standalone-compaction failure is non-fatal for Codex (that turn's compaction is skipped; the loop continues on full history) but a HARD failure for native `mimo` (it propagates to the caller — never silently continuing on full history and never falling back to a different wire), because MiMo has no generic `context_management` fallback and no server-side state to lean on. Omit to use the daemon session's own resolved context window as the threshold: explicit preset canonical `manifest.llm.context_limit` when supplied, otherwise the inherited valid parent effective window, otherwise 272,000. Must be a positive integer; a boolean is rejected.
+ Every terminal outcome (`done`, `failed`, `cancelled`, `timeout`) is
+ push-notified exactly once. Do not poll for "is it done". After notification,
+ call `check` and open the durable `result.txt`/error path for full output;
+ previews and notification text are bounded. `list` is a bounded ledger sweep,
+ not a lifetime folder scan. `check` accepts a live id or exact historical run
+ id and returns state, events, paths, and an artifact manifest. Read inspection
+ before declaring a stall or reclaiming on a hunch.
- - `compact`: automatic for every LingTai daemon and absent from external CLI
- backends. `action` is required; omission or any value other than explicit
- `"run"`/`"manual"` is refused without changing context. For execution, call
- `compact(action="run", _reason="...")` as the sole tool call in a batch with
- a non-empty complete self-contained handoff. All previous provider-visible
- history is removed; only the compact assistant call/result pair and rebuilt
- system prompt survive. The surviving result is stamped from the fresh
- retained context, so a pre-reset >=90% warning/countdown clears. The result
- includes exact run/state/history/event paths. It is repeatable and non-terminal.
- - `compact(action="manual")`: read-only procedures for compaction. It never
- resets context, requires no `_reason`, and may be called to inspect the
- procedure before deciding whether to compact. The daemon runtime starts a
- deterministic nine-round countdown on the first provider round at or above
- 90%, starting at visible value `9` and decrementing through `1`. The `warning`
- and `compact_countdown_warning` fields carry one self-contained sentence for
- the current value: `Daemon context is at or above 90%. {N} proactive round(s)
- remain before runtime mechanical compact; call compact(action="run",
- _reason="...") now to compact with your own handoff.` Values decrement only
- while above `1`; value `1` remains visible through one ordinary provider
- response. On the next high response, the runtime latches expiry while
- preserving visible `1`; if that response does not issue a valid sole-call
- proactive compact, mechanical compaction occurs before the next provider
- continuation. On any drop below 90%, the countdown and warning reset/disappear.
- Mechanical compaction retains the system prompt and latest assistant/tool-result
- pair, then sends an explicit recovery message. Before continuing, re-read the
- task, inspect that preserved pair and durable run state/history/event paths,
- verify state, and only then resume; never silently continue after context loss.
- Mechanical compaction failures propagate as daemon failures.
- - Every LingTai daemon tool batch ends with a canonical `_meta.agent_meta`
- snapshot on its newest final `ToolResultBlock`. Read `agent_state.daemon` for
- run/round identity, `agent_state.token_usage` for current-call and cumulative
- counters, and `agent_state.context` for provider context tokens/window/ratio.
- This is daemon-local state: parent notifications and communication context are
- intentionally absent, and no main-agent resident-guidance reference is invented.
- Only the latest `agent_meta` is current; older snapshots are historical traces.
- While current context usage is at or above 90%, every daemon round carries the
- self-contained warning sentence plus the visible countdown fields; after a
- successful compact or any other drop below 90%, the warning and countdown
- disappear. Provider-specific automatic standalone
- compaction remains independent of this daemon-owned countdown.
+ For large batches, use `group_id` for logical audit but each run id for filesystem
+ identity. A Task Card is conditional on the dispatch handoff: use it only when
+ Telegram is connected and a card exists; daemon itself creates no watcher.
- - Treat `task` as the parent's behavioral contract for **all** tools
- and selected skills/MCP context, not only for communication. If a daemon receives `shell`,
- say whether it may run mutating commands; if it receives file access, say what
- it may read/write; if it receives web/MCP tools, say what external calls are
- allowed; if `skills` or `mcp` are selected, say when to read/apply/call them.
- For `email` specifically — daemon-eligible but opt-in, granted only with
- `tools: ["email"]` — availability is not authorization to broadcast: state the
- allowed recipients, purpose, thread/reply discipline, information boundaries,
- whether the daemon may ask questions or only report, and how to report back to
- the parent.
- - LingTai-backend daemon tool calls go through the kernel `ToolExecutor` /
- `ToolCallGuard` path before dispatch, so guarded side effects are not allowed
- to bypass normal proposal/execution policy just because they run in a daemon.
- - Every `daemon.emanate` call returns a batch `group_id` shared by all daemon
- runs launched in that same call. Use `group_id` for logical batch context and
- audit. It is not a hard security boundary; use each daemon's `run_id` for
- per-run filesystem/audit identity.
- - Track daemon work in the parent agent's pad, not in daemon itself. When you
- fan out multiple tasks, immediately write a small pad table after `emanate`:
- label/purpose, returned `id`, `group_id`, brief/context file path, expected
- artifact, and current status. Use `daemon(action="list", input={})` and
- `daemon(action="check", input={"id": ...})` as the mechanical truth, then update the pad
- as the parent-facing map. Daemon should stay thin; if you need durable memory
- or identity, use an avatar instead.
- - Do not copy large background into every task. Put reusable context in a
- brief/report/notes file and pass that file path explicitly in the `task`
- (with file access if the daemon should read it). A follow-up daemon should
- consume visible artifacts such as the previous task prompt, result file,
- report, event summary, or context files; do not treat a daemon as a resumable
- mind or hidden-context container. Prefer making daemon history searchable and
- easy to point at over copying or reviving a daemon session.
- - Each emanation is disposable memory but durable evidence: its folder persists
- after completion or reclaim until cleanup.
- - `daemon(action="list", input={})` is the first layer of progressive disclosure over
- active and historical runs — compact metadata, previews, and paths, not a full
- transcript. Use the returned paths for detail; see
- `reference/cli-backends/SKILL.md` for its filters and lazy-rebuild behavior.
- ### Selected detached Shell async jobs
+ ## Compact and completion boundaries
- A selected `shell` in a detached LingTai daemon is **not** normal Agent Shell
- notification delivery. The daemon has no parent Agent notification store,
- mailbox, heartbeat, or `.notification/system.json` / `.notification/bash.json`
- write. Its Shell state lives under that run's private `<run>/shell-jobs`, while
- commands still use the granted task workdir; it never rehydrates or polls parent
- or sibling daemon jobs. Shell reminder/completion publications become bounded,
- durable events in that daemon run only. A full queue is retried by the live
- selected Shell manager with capped backoff after a normal safe-boundary drain.
- At a later safe provider-send boundary while the daemon is still live, fixed
- guidance names the job id and tells the daemon to call `shell.poll` for exact
- output. Output is not placed in the prompt.
+ Every LingTai daemon receives intrinsic `compact`; external CLIs do not. Its
+ `action` is required: `manual` is read-only, while `run` is a repeatable
+ non-terminal reset. Call `compact(action="run", _reason="...")` as the sole
+ assistant-batch tool call with a complete self-contained handoff. Never call the
+ unavailable parent `system.summarize`.
- Do not wait for an event, auto-poll, assume a parent wake per Shell job, or end
- a run expecting re-entry: an event cannot hold open, restart, or resurrect a
- terminal daemon. The supervisor's ordinary terminal receipt is still the only
- final parent wake. This path is distinct from a `daemon_common` checkpoint and
- from normal Agent Shell's `.notification` behavior.
+ MCP-capable backends also receive built-in `daemon_common`. Call its
+ `checkpoint` at useful nonterminal boundaries; it is not chat, cancellation,
+ preemption, or completion. Before ending, call `finish` exactly once. Only
+ `finish(status="done")` permits terminal success. Background-and-wait is
+ invalid: run validation synchronously with an adequate explicit timeout and
+ inspect the result in the same run. Missing/invalid completion is a failure;
+ inspect durable artifacts before assuming anything was lost.
- - **Every terminal outcome is push-notified exactly once** — done, failed,
- cancelled, or timed out. After you dispatch, you can safely go IDLE and wait
- for the notification; do not poll only to ask "is it done yet". The
- notification arrives on the system channel carrying the daemon id, terminal
- status, task summary, and the result/error path. React to it with
- `daemon(action="check", input={"id": ...})` (and read `result.txt` for the full output).
- - **A cooperative checkpoint is a separate nonterminal wake.** On one, call
- `daemon(action="check", input={"id": ...})` to inspect `latest_checkpoint`
- and the `pending_checkpoint_messages` count. If a correction is needed, use
- `daemon(action="ask", input={"id": ..., "message": ...})`. A
- `{status: "queued", delivery: "checkpoint", message_id: ...}` receipt means
- the message waits durably for the model's next checkpoint; it is not live
- chat or immediate CLI input. This active-run route exists only for
- `claude-p`/`claude-code`, Codex, OpenCode, Qwen, and Kimi because those exact
- launch paths mount `daemon_common`; hidden interactive Claude, MiMo,
- Oh-My-Pi, and Cursor remain `busy` while active. Qwen/Kimi still have no
- terminal resume route. Do not poll waiting for checkpoints — the model chooses
- useful boundaries, and terminal notification discipline remains unchanged.
- - **Use a Task Card for progress when one is available for this turn.**
- The dispatch success `handoff` is conditional: if Telegram is connected and a
- Task Card is available for the current turn, use it to report progress — call
- `telegram(action='manual')` and follow its `Programmable Task Card` section.
- The daemon tool does not create a Task Card automatically or require a
- watcher; daemon lifecycle and terminal-notification behavior are unchanged.
- A card-worthy dispatch — two or more tasks, or an explicitly requested
- `timeout` of 900s or more — appends one extra nudge sentence to `handoff`
- when you have no active watch: start one with `task_card(action='start')` so
- a human can follow the fleet instead of watching it run dark. The nudge
- disappears once a watch is running.
- - **`check` still resolves a daemon after refresh/molt.** A refresh/molt gives
- you a fresh daemon registry with no in-memory entries, but the run folders
- and their notifications survive on disk. New daemon ids are compact run ids
- such as `em-a1b2` (or `em-a1b2-1` after a collision), and
- `daemon(action="check", input={"id": ...})` exact-matches that `daemons/<run_id>/` folder
- on a registry miss. Legacy short handles such as `em-5` are accepted only when
- they resolve to one historical run; if several old runs share the handle,
- `check` returns an ambiguity error with `match_count`/`latest_run_id` instead
- of an unbounded path list. Use the exact `run_id` from the notification or
- `daemon(action="list", input={})` when a legacy handle is ambiguous.
- - **Defense-in-depth, not primary signal: a self-wake guards against a daemon
- that never reaches a terminal state at all.** The terminal notification covers
- every state a run can *finish* in, but a run that hangs without the watchdog
- firing, or a degraded notification-wake path, could leave you waiting forever.
- When daemon work is pending and unverified-healthy, arm one self-wake sized to
- the task's expected duration as a backstop, then health-check on wake and
- reclaim/downgrade/switch path rather than waiting indefinitely. Do not turn
- this backstop into frequent polling. Procedure:
- `reference/inspection/SKILL.md`.
- - If repeated-call `_advisory` appears on `daemon(list/check)`, the call still
- ran; treat it as a signal to stop the loop, centralize status checking in the
- parent, and read `reference/inspection/SKILL.md` before polling again.
- - If an emanation might be stuck, inspect state changes, recent transcript, and
- event activity before reclaiming.
- - CLI backend flags are passthroughs. Verify the current CLI's `--help` before
- relying on a flag.
+ ## Safety, Shell events, and footprint
- ### Example: separate task from behavior guidance
+ Keep the parent task's authorization and privacy boundary authoritative. Do not
+ use daemon for durable identity, automatic recursion, or hidden context. A
+ selected daemon `shell` has a private `<run>/shell-jobs` namespace and no parent
+ notification store; read `shell-manual` for async + poll supervision. Do not
+ reclaim on a hunch: it terminates work in flight but keeps run folders as
+ evidence. There is no automatic cleanup.
- Put the deliverable and the daemon's operating contract together in `task`.
- Use `prompt` only when LingTai needs a custom first ordinary user message:
+ Inspection, forensics, dispatch-ledger, cleanup, shell-event, programmatic-use,
+ and no-deletion routes remain available through the references above. Never
+ permit deletion, global configuration/install changes, account/provider changes,
+ or cleanup outside an authorized scope.
- ```json
- {
- "task": "Act as a documentation reviewer. Stay read-only except for the requested report file. Use the selected daemon-manual skills only when you need exact daemon semantics. Use the local-docs MCP only for daemon documentation lookup, not for unrelated search. You may use email only to ask dev-2 for missing daemon context; do not contact the human. If you email dev-2, state the exact question, include only the relevant snippet, and summarize the exchange in your final report. Do not use web tools unless the local docs are insufficient. Deliverable: audit the daemon manual changes and write a concise review to reports/daemon-manual-review.md.",
- "prompt": "Begin the documentation review.",
- "tools": ["file", "shell"],
- "mcp": [
- {"name": "local-docs", "transport": "stdio", "command": "python", "args": ["-m", "local_docs_mcp"]}
- ],
- "skills": [
- "src/lingtai/tools/daemon/manual",
- "src/lingtai/tools/daemon/manual/reference/cli-backends/SKILL.md"
- ]
- }
- ```
+ ## Programmatic use / CLI
- `tools` grants a capability surface, `skills` a selected workflow catalog, `mcp`
- one-run registrations; `task` tells the daemon how to exercise all of them in
- this one run.
+ For scripts and CI, use `lingtai-agent daemon --help` for the current command
+ surface and options.
## Maintenance
- Keep this router short. Put new backend recipes, inspection examples, and cleanup
- procedures in nested references so agents load only the needed detail.
+ Keep this file a router. Put backend command recipes and reserved flags in the
+ per-backend references; keep inspection, forensics, ledger, shell, and cleanup
+ procedures there. Keep the action and settings anchors stable as deeper owners
+ move.