kestrel-feature-talon
Portable Kestrel feature integration and runtime contracts for the external Talon coding-agent engine.
Package boundary
This distribution and the Talon engine have separate ownership:
| Component | Distribution / command | Owns |
|---|---|---|
| Talon feature | kestrel-feature-talon |
Kestrel SDK feature discovery, agent tools, permissions, Talon-specific completion/pipeline/fleet-coding sources, the talon:<job_id> wait provider, health checks, and setup integration |
| Workflow runtime | kestrel-feature-workflows |
Public workflow models and execution contracts used by the fleet workflow registered and built by this feature |
| Talon engine | kestrel-talon / kestrel-talon |
Issue processing, coding-agent execution, and its command-line runtime |
Runtime health is exposed through the talon_health tool. The feature's
synchronous health() method is a direct convenience probe; it is not a
registered SDK health hook.
The 0.2 operator API supports launch, observation, pause, resume, and cancel.
It deliberately does not support stage retry: Workflows 0.5 cannot pass fresh
catalog/launch authority through its generic retry seam, so a generic retry of
a failed Talon stage is terminalized without another engine dispatch. Launch a
new governed run instead. Result fields for tests, review, demo, and Eye remain
unknown/not_reported unless Talon supplies a bounded verified producer
contract. Only Talon's bounded final claim-summary contract can populate PR
identity today; arbitrary or partial log text is never promoted to evidence.
Installing this feature also installs its compatible workflow runtime
dependency. It does not install or bundle the Talon engine. The host must install
kestrel-talon separately and either put its executable on PATH or configure
its absolute path as described below.
The compatibility ladder is intentional: Talon feature 0.2.0 is the migration
bridge for Sovereign 0.52, where the bundled predecessor still exists. Talon
feature 0.2.1 is the sole-owner release for Sovereign 0.53 and requires
kestrel-sovereign>=0.53.0,<0.54.0; 0.53 supplies the public turn-bound session
adapter needed to preserve durable completion wakes without reaching into core
lifecycle internals.
Supported platforms: POSIX only
Linux and macOS, as pyproject.toml's classifiers say. This is a requirement
of CLI dispatch rather than a preference: a dispatched job is launched as an
sh wrapper script and identified for its whole lifetime by an inherited
flock descriptor, and neither has a stock Windows form. Off POSIX no job can
be dispatched, so no job exists to observe, pause, resume or cancel.
Nothing downstream of dispatch therefore carries a sys.platform branch. The
cancel, pause/resume and process-teardown paths used to, and those branches
were removed rather than brought into line: they could only ever have run
against a job that could not have been launched, while reading — to the next
person auditing the stop path — as a support claim this package does not make.
Adding Windows support would start at dispatch (a wrapper that is not sh) and
at identity (a portable lock in place of fcntl), not at termination.
Install and enable
Install the feature into the same Python environment as Kestrel Sovereign:
python -m pip install kestrel-feature-talon
Kestrel discovers TalonCoordinatorFeature from the standard Sovereign
feature entry-point group. The wheel also publishes a pre-boot setup provider
and the fleet_coding_pipeline workflow-to-feature ownership claim required by
Sovereign 0.53. Capture the installed package
in the host feature manifest so a later uv sync can be repaired, enable the
feature if it was disabled, and restart the affected agent after enablement:
kestrel feature sync --capture
kestrel feature enable talon
On a host whose .kestrel-host-features.toml already declares the package,
kestrel feature sync installs or restores it; --capture is only needed when
first recording the currently installed extensions.
Run the optional Kestrel setup step to collect the engine's GitHub credentials:
kestrel setup talon
The setup step writes GITHUB_TOKEN and optional GITHUB_HUMAN_REVIEWER
values to the host project's .env, preserving unrelated values and backing
up an existing file before a change. It does not install the engine or choose
runtime paths.
Runtime configuration
Paths are explicit and installation-independent. Configure them under
[talon.runtime] in a host-selected TOML file, or provide the equivalent
values through the host context:
[talon.runtime]
engine_executable = "/opt/kestrel/bin/kestrel-talon"
project_parent = "/srv/kestrel/projects"
running_agent_source_root = "/srv/kestrel/kestrel-sovereign"
The host supplies the TOML filename through the feature's config_path; the
package does not select a global configuration file. A host may instead place
the runtime mapping directly in the feature configuration or public
talon_host_context.
Feature configuration applied through the SDK/HTTP configuration surface is
stored atomically as talon_feature_config.toml with mode 0600 beside the
stock agent's storage_path. On initialization, an explicit talon_config on
the agent or its talon_host_context takes precedence; otherwise the feature
loads that package-owned file. The file stores only the feature's config_path
and direct runtime mapping—the host-selected TOML remains independently
owned. A corrupt persisted file is reported as a configuration error and no
runtime paths are inferred in its place.
Making fleet_coding_pipeline runnable: pipeline_mode
Contributing the pipeline is not enough to run it. workflow_run loads
definitions from the agent's workflow store, and nothing consumes a
contributed WorkflowRegistration into that store. An agent that dispatches
the pipeline — Strategic Memory's signal_dispatch does, by default — opts in
per agent:
# agent_data/<Agent>/kestrel.toml
[features.talon.config]
config_path = "/srv/kestrel/kestrel.toml"
pipeline_mode = "autonomous" # or "consent"
At ready time the feature defines the chosen variant into that agent's own
store through the governed workflow_define tool, signed with the agent's
identity, and only if no live definition exists. consent keeps the
approve_dispatch stage, so every run waits for a human in the Approvals tab
before Talon writes code; autonomous omits it, and the safety moves
downstream to the merge gate. With pipeline_mode absent nothing is defined —
there is deliberately no default, because either default would be a signed
write to every agent's store that never asked for one.
It runs off the boot path and calls as a feature lifecycle hook (session id
feature-lifecycle). On an agent whose workflow tools require approval, a
Sovereign that knows that id refuses the define at once rather than queueing
an approval nobody is there to answer — logged at ERROR; grant the workflows
tools ALLOW or define the pipeline once by hand. An older Sovereign queues
the approval instead, which is answerable in the Approvals tab because boot
has not waited on it.
It only ever adds a definition. To switch variant, revoke the live one
(workflow_revoke_definition) and restart. A live definition of the other
variant is reported by what it means: configured consent while the live
pipeline is autonomous is an ERROR — runs are not waiting for anyone. A failed attempt is logged at
ERROR naming the consequence, because the silent version of this failure left
one agent's daily dispatch failing for 23 days (sovereign #3280).
Agents without a concrete string or pathlib.Path storage_path still accept
configuration for the current process, but that configuration is deliberately
ephemeral and will not survive restart. Generic os.PathLike objects and test
doubles are not treated as state locations.
engine_executable takes precedence. When omitted, the feature performs only
a normal PATH lookup for kestrel-talon; it never searches relative to its
installed package or assumes sibling checkouts. project_parent and
running_agent_source_root never have implicit filesystem defaults.
Talon workspace clones are kept under
<project_parent>/.talon-workspaces/<owner>__<repo>. Every dispatch and
verification operation rejects a workspace that contains, equals, or sits
inside running_agent_source_root.
The package's TalonConfigAdapter reads and atomically updates the selected
TOML file while retaining unrelated configuration values. Hosts may attach a
talon_host_context mapping or object to the owning agent. In addition to the
runtime paths above, the feature recognizes public context values for
agent_name, did, storage_path, scheduler, observability_store,
wait_registry, current_signal (or get_current_signal()), and
origin_session_id (or get_origin_session_id()). This adapter keeps
workflow/session correlation and durable job storage independent of private
Sovereign agent fields. When that explicit context does not define an origin,
Sovereign 0.53's public agent.get_turn_bound_session_id() adapter supplies the
live turn's session; no private lifecycle compatibility alias is consulted.
Tool and contribution contract
Version 0.2.1 preserves every tool of the public contract extracted from
Kestrel Sovereign 0.52.0, and adds talon_cancel:
| Tool | Command prefix |
|---|---|
scan_stale_work |
!talon scan-stale-work |
talon_claim |
!talon claim |
talon_file_and_claim |
!talon file-and-claim |
talon_github_write |
!talon github-write |
talon_get_config |
!talon config |
talon_set_config |
!talon set-config |
talon_verify |
!talon verify |
talon_schedule_work_rescue |
!talon schedule-rescue |
talon_workspace_status |
!talon workspace-status |
talon_setup_workspace |
!talon setup-workspace |
talon_batch |
!talon batch |
talon_status |
!talon status |
talon_job_log |
!talon job-log |
talon_pause |
!talon pause |
talon_resume |
!talon resume |
talon_health |
!talon health |
talon_cancel |
!talon cancel |
The 0.52.0 tool names, command prefixes, and parameter schemas remain
compatible; the snapshot of that surface is a migration fidelity check, not a
ceiling. talon_cancel stops one dispatched job's whole process group and
records the outcome. Every talon_claim is dispatched as such a group:
earlier releases preferred an A2A rail for an unattended claim with nothing
customised, and a job dispatched there had no supported stop, because the A2A
cancel endpoint marks the remote task canceled without reaching the process
doing the work. That rail was removed rather than left half-cancellable;
delegating a claim over A2A would return only together with a real stop
contract. talon_cancel still refuses, fail-closed, any job that is not a
local CLI process group — including a job talon_status listed from
observability rather than from this coordinator's registry — and says so in its
own error rather than reporting the job as unknown, because "no such job" reads
as "already gone". When the group is provably stopped but the outcome
could not be durably recorded, it reports cancel_unrecorded as a partial
result instead of claiming the job was cancelled, and a retry re-attempts only
the recording. Every signal it sends, escalation included, goes through the one
function in the package able to signal a process group at all, which authorizes
each signal against the job's group-owned identity re-read at that instant. So a
group that exits during the SIGTERM grace period is never escalated onto through
its reissued PGID, and a job dispatched without an identity is refused rather
than signalled on the strength of a recyclable number. A job whose engine
exited cleanly while something it started still holds the job's identity keeps
reporting as running, from a restarted agent exactly as from the one that
launched it, because a recorded exit describes the worker and not the job. That
outcome is not discarded — it is applied once no holder of the identity is
left. A job whose liveness cannot be established is reported as running with
liveness "indeterminate" rather than as finished.
The bound on all of this is deliberate and worth knowing: liveness answers for
the dispatch wrapper and the worker, which are what hold the job's identity. A
descendant the engine starts with close_fds=True joins the process group
without inheriting that identity, so once both holders have exited such a
descendant is not observed — the job reports finished from the outcome the
wrapper recorded while that descendant may still be running. Observing it would
mean falling back on the bare, recyclable PGID, which is the thing the identity
exists to replace; job_identity's module docstring records why the bound is
accepted rather than widened. The
talon_schedule_work_rescue description and behavior are an intentional
Sovereign 0.53 disclosure correction: the command now refuses a schedule that
cannot perform live Talon discovery instead of claiming a recurring rescue was
installed. talon_claim gains two additive parameters, resume and
resume_branch; every predecessor parameter is unchanged and both new ones are
optional.
A finished job's talon.job_complete wake also carries its disposition —
completed, clarifying, blocked, failed, or unknown — read from the
processing summary Talon prints rather than from the exit code, because Talon
exits 0 for a run that stopped to ask a question. A clarifying or blocked
run is reported with that word and its question, not as completed, and the
wake tells the orchestrator to answer on the issue, clear Talon's gate
(agent-blocked, or 👍 / "ready" / agent-ready for a clarifying run), and
re-dispatch from its own loop. A summary that cannot be read is unknown,
never completed. A talon_pipeline_dispatch workflow stage that waits on
the run applies the same rule: it reports state: "complete" (with its
disposition) only for a completed run, and fails the stage closed —
naming the disposition and the question — for any other.
The same dispositions are what kestrel-sovereign's signal_dispatch reads,
through the wait provider's read-only finished_runs(), to avoid
re-dispatching an issue whose last run asked a question nobody has answered.
That read reports whether it covered every job in the registry. A registry
that exists but cannot be read whole — corrupt or oversized jobs.json, a
record it cannot interpret, or a finished job whose log is gone — is reported
incomplete with the reason, and dispatch stops with RUN_HISTORY_UNCONFIRMED
rather than treating it as no history. An agent that has never dispatched a
Talon job has no registry yet, and that is an empty history.
The feature also contributes the agent-scoped talon.coordinator service,
the talon:<job_id> wait provider, Talon completion/pipeline/fleet-coding
workflow sources, conservative permission defaults, and the optional setup
step. Generic stalled_work_rescue sources remain host infrastructure; Talon
does not register or override them. Sovereign 0.53's generic sweep is
echo-only, so talon_schedule_work_rescue refuses to create a recurring no-op
and points operators to scan_stale_work for live Talon findings.
scan_stale_work is allowed for unattended discovery; every other Talon tool
defaults to operator approval.
talon_pause writes a private durable dispatch marker and CLI dispatch checks
it before any external effect. The marker survives agent restarts;
talon_resume clears it explicitly. These tools do not depend on a retired
core scheduler task. talon_file_and_claim treats its audited GitHub issue
creation and Talon launch as two explicit boundaries: a pre-existing pause
prevents the GitHub write, while a pause requested during approval or issue
creation takes effect immediately and blocks the later dispatch. In that race,
the tool reports the already-filed issue as a partial outcome for manual review.
Preserved work from a failed run
When a run fails, kestrel-talon commits what the agent produced and keeps the
checkout. talon_claim(resume=True, resume_branch=...) continues that checkout
rather than preserving it aside and branching fresh — the two coordinates of
the recovery command a failed run prints. They arrive together on purpose:
resume without a branch continues whichever issue-<n>-* branch the checkout
happens to be on, which is the hole resume_branch closes. The tool carries
the engine's own refusals instead of relying on a background subprocess exiting
2 where nobody reads it, so resume without a worktree, resume without a
concrete issue, and resume_branch without resume (including an empty one)
are refused with nothing dispatched. This is unrelated to talon_resume, which
un-pauses dispatch.
Two behaviors around that checkout are the feature's own:
- A fresh claim is refused when a previous attempt at the same issue holds
work that exists nowhere else — unpushed commits, or an uncommitted tree.
Talon preserves such a checkout aside rather than deleting it, so nothing is
destroyed either way; what the refusal prevents is the work moving one
talon-preserved-*directory further from anyone looking for it with no trace of that in the dispatch result. The refusal names the checkout, its branch, and the exactresumecall that continues it. Work that is already pushed never blocks a claim. - When a job ends without succeeding, its preserved branch is pushed, so the commits stop being the only copy. Never forced, never committed on the run's behalf, and never against a branch that does not belong to the job's issue; a job still holding its group-owned identity is left alone until it is really over. The outcome — including a rejected push — is recorded on the job record rather than left to be inferred.
Both act on the branch the checkout is actually on, re-read from git, never on the directory name that suggested looking there.
The host-scoped TalonHostFeature owns /api/talon, a capability-gated Talon
panel, lifecycle workers, and the talon.operator service. Manual claim and
approved-batch launches accept only opaque values resolved through
claws.catalog>=1.1.0, while durable run state and artifacts are projected
through workflows.runs>=1.0.0. Configure the same runtime paths under
[talon.runtime]; operator_state_path may additionally select the private
checksum-bound intent/evidence root. If omitted, it is placed beneath the
explicit project_parent.
Hosts may inject a Castle or Workflows operator_context_resolver; that
resolver always takes precedence. Without one, the operator API resolves the
authenticated single-tenant policy through claws.catalog>=1.3.0. Missing or
incompatible policy services fail closed.
Referenced operator evidence is never evicted behind an active or completed run API. The private store instead enforces a fail-closed retention ceiling of 10,000 run directories and 128 MiB of evidence; once either bound is reached, new artifact finalization is refused until an operator archives the state root or provisions a larger policy in a future release. Expired pre-dispatch intents are pruned off the event loop, while dispatched and linked intents remain recoverable until their external job is finalized.
Development
uv lock --check
uv run --extra test pytest -q
uv build
git diff --check
The wheel and source distribution are built from this repository alone. No sibling checkout or editable/path dependency is part of the package metadata.
Metadata
Release files for kestrel-feature-talon 0.2.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kestrel_feature_talon-0.2.10.tar.gz | 515.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kestrel_feature_talon-0.2.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 734.4 kB
Release files / kestrel_feature_talon-0.2.10.tar.gz
| Download URL | kestrel_feature_talon-0.2.10.tar.gz |
|---|---|
| Size | 515.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0fefc57102301c494ed01906f9bb8e5e39f54515e3d775156fd8e43a04bc248
|
|
BLAKE2b-256 checksum How to use checksums |
5f9858828b9585421a530b8fa7cd37f8f81068d5967c2b095bccc425782dfd02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency logRelease files / kestrel_feature_talon-0.2.10-py3-none-any.whl
| Download URL | kestrel_feature_talon-0.2.10-py3-none-any.whl |
|---|---|
| Size | 219.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a7c9ea8d1ef8249c3c5f8576d505a326399e155633e60bc04aaf8d5e036d7a93
|
|
BLAKE2b-256 checksum How to use checksums |
3b9c0931d62f85f070cd4aa3560df80fad1eb2ce85d9f40590ea22d500c2547c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency log