@telora/daemon 0.24.5 → 0.24.8
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/README.md +11 -3
- package/build-info.json +10 -3
- package/dist/backends/agent-backend.d.ts +10 -0
- package/dist/backends/agent-backend.d.ts.map +1 -1
- package/dist/backends/claude/claude-backend.d.ts +19 -0
- package/dist/backends/claude/claude-backend.d.ts.map +1 -1
- package/dist/backends/claude/claude-backend.js +41 -1
- package/dist/backends/claude/claude-backend.js.map +1 -1
- package/dist/backends/codex/codex-backend.d.ts.map +1 -1
- package/dist/backends/codex/codex-backend.js +50 -20
- package/dist/backends/codex/codex-backend.js.map +1 -1
- package/dist/backends/index.js +1 -1
- package/dist/cli/env-command.d.ts +21 -0
- package/dist/cli/env-command.d.ts.map +1 -0
- package/dist/cli/env-command.js +42 -0
- package/dist/cli/env-command.js.map +1 -0
- package/dist/completion/review-exit/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit/review-exit-phase.js +11 -22
- package/dist/completion/review-exit/review-exit-phase.js.map +1 -1
- package/dist/composition/cli-env-file-context.d.ts +4 -0
- package/dist/composition/cli-env-file-context.d.ts.map +1 -0
- package/dist/composition/cli-env-file-context.js +27 -0
- package/dist/composition/cli-env-file-context.js.map +1 -0
- package/dist/composition/execution-migrations-context.js +1 -1
- package/dist/composition/runtime-poll-context.d.ts +7 -1
- package/dist/composition/runtime-poll-context.d.ts.map +1 -1
- package/dist/composition/runtime-poll-context.js +14 -2
- package/dist/composition/runtime-poll-context.js.map +1 -1
- package/dist/config/env-file.d.ts +37 -0
- package/dist/config/env-file.d.ts.map +1 -0
- package/dist/config/env-file.js +85 -0
- package/dist/config/env-file.js.map +1 -0
- package/dist/daemon-state/daemon-process.d.ts +10 -0
- package/dist/daemon-state/daemon-process.d.ts.map +1 -1
- package/dist/daemon-state/daemon-process.js +24 -15
- package/dist/daemon-state/daemon-process.js.map +1 -1
- package/dist/delivery/delivery-reconciler.d.ts +10 -15
- package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler.js +17 -30
- package/dist/delivery/delivery-reconciler.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +2 -6
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/verify-routing-passes.d.ts +5 -1
- package/dist/focus/verify-routing-passes.d.ts.map +1 -1
- package/dist/focus/verify-routing-passes.js +1 -0
- package/dist/focus/verify-routing-passes.js.map +1 -1
- package/dist/host-control/load.d.ts.map +1 -1
- package/dist/host-control/load.js +2 -1
- package/dist/host-control/load.js.map +1 -1
- package/dist/index.js +14 -7
- package/dist/index.js.map +1 -1
- package/dist/prompt/team-prompt-base.d.ts +31 -2
- package/dist/prompt/team-prompt-base.d.ts.map +1 -1
- package/dist/prompt/team-prompt-base.js +52 -2
- package/dist/prompt/team-prompt-base.js.map +1 -1
- package/dist/prompt-sections/persona-sections.d.ts +7 -6
- package/dist/prompt-sections/persona-sections.d.ts.map +1 -1
- package/dist/prompt-sections/persona-sections.js +17 -13
- package/dist/prompt-sections/persona-sections.js.map +1 -1
- package/dist/prompt-sections/role-sections.d.ts +12 -1
- package/dist/prompt-sections/role-sections.d.ts.map +1 -1
- package/dist/prompt-sections/role-sections.js +42 -1
- package/dist/prompt-sections/role-sections.js.map +1 -1
- package/dist/prompt-sections/shared.js +1 -1
- package/dist/queries/deliveries/falsified-effect-reopens.d.ts +33 -0
- package/dist/queries/deliveries/falsified-effect-reopens.d.ts.map +1 -0
- package/dist/queries/deliveries/falsified-effect-reopens.js +36 -0
- package/dist/queries/deliveries/falsified-effect-reopens.js.map +1 -0
- package/dist/resolvers/assembly-engine.js +1 -1
- package/dist/resolvers/git-diff-against-base.d.ts.map +1 -1
- package/dist/resolvers/git-diff-against-base.js +2 -1
- package/dist/resolvers/git-diff-against-base.js.map +1 -1
- package/dist/session/session-lineage.d.ts +15 -6
- package/dist/session/session-lineage.d.ts.map +1 -1
- package/dist/session/session-lineage.js +17 -5
- package/dist/session/session-lineage.js.map +1 -1
- package/dist/shell/unified-shell.d.ts.map +1 -1
- package/dist/shell/unified-shell.js +11 -5
- package/dist/shell/unified-shell.js.map +1 -1
- package/dist/spawn/model-routing.d.ts +1 -1
- package/dist/spawn/model-routing.d.ts.map +1 -1
- package/dist/spawn/model-routing.js +6 -4
- package/dist/spawn/model-routing.js.map +1 -1
- package/dist/spawn/prompt-assembly.d.ts +0 -9
- package/dist/spawn/prompt-assembly.d.ts.map +1 -1
- package/dist/spawn/prompt-assembly.js +61 -65
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts +8 -1
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +10 -3
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/tower/tower-mode.d.ts +17 -5
- package/dist/tower/tower-mode.d.ts.map +1 -1
- package/dist/tower/tower-mode.js +27 -3
- package/dist/tower/tower-mode.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/verify/effect-reconciliation/verify-reconciliation.d.ts +34 -5
- package/dist/verify/effect-reconciliation/verify-reconciliation.d.ts.map +1 -1
- package/dist/verify/effect-reconciliation/verify-reconciliation.js +106 -27
- package/dist/verify/effect-reconciliation/verify-reconciliation.js.map +1 -1
- package/dist/verify/outcome-routing/falsified-effect-reopen.d.ts +17 -0
- package/dist/verify/outcome-routing/falsified-effect-reopen.d.ts.map +1 -0
- package/dist/verify/outcome-routing/falsified-effect-reopen.js +76 -0
- package/dist/verify/outcome-routing/falsified-effect-reopen.js.map +1 -0
- package/docs/runbook-tower.md +237 -0
- package/package.json +8 -5
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Runbook: the product tower
|
|
2
|
+
|
|
3
|
+
The tower is traffic control for a product's armed foci. It changes what
|
|
4
|
+
ARMED means -- from "run now" to "run when cleared" -- and it never arms or
|
|
5
|
+
un-arms anything: arming stays the human go-signal. Decision record:
|
|
6
|
+
`docs/DECISIONS.md` ("The tower: traffic control for armed foci lives at the
|
|
7
|
+
product").
|
|
8
|
+
|
|
9
|
+
With the tower in `enforce`, an operator can arm several foci of a product,
|
|
10
|
+
set the product's limit to 1, and walk away: they run one at a time, in focus
|
|
11
|
+
rank order.
|
|
12
|
+
|
|
13
|
+
## The pieces
|
|
14
|
+
|
|
15
|
+
| Piece | Where | What it is |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| The limit | `products.tower_concurrency_limit` | How many of the product's foci may hold a live coding or review team at once. `NULL` = no product limit: the host session cap alone bounds it. Set with `telora_product` `update` `towerConcurrencyLimit` (`null` clears it). |
|
|
18
|
+
| The verdict | `product_focuses.tower_verdict` | The tower's verdict on one armed, active focus: `cleared`, or `holding` with exactly one reason and since-when. |
|
|
19
|
+
| The pass | `packages/daemon/src/tower/tower-pass.ts` | One pass per configured product; the SOLE writer of verdicts. |
|
|
20
|
+
| Admission | `packages/daemon/src/tower/tower-admission.ts` | What the spawn passes ask before they start a coding or review team. |
|
|
21
|
+
| The graph | `focus_tower_edges` | Declared `depends_on` / `excludes` edges between two foci of the product, with provenance and evidence. Written over MCP (`telora_product_tower`); shown on the product page's Focus tab. |
|
|
22
|
+
| Landed | `packages/daemon/src/tower/focus-landed.ts` | The ONE definition of "this focus's work has landed", shared with the PRD frontier. |
|
|
23
|
+
| The vocabulary | `@telora/daemon-core` `tower.ts` | Modes, verdict states, reasons (in precedence order), edge kinds and provenance, and the fail-open freshness rule -- one definition for the daemon and the dashboard. |
|
|
24
|
+
|
|
25
|
+
## The verdict
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
{ state: 'cleared' | 'holding',
|
|
29
|
+
reason: null | 'product_limit_reached' | 'waits_on' | 'excluded_by',
|
|
30
|
+
since: ISO, -- when this state + reason began (kept across re-confirmations)
|
|
31
|
+
computedAt: ISO, -- when the pass last confirmed it
|
|
32
|
+
armedAt: ISO|null, -- the arm (agent_assigned_at) it was computed under
|
|
33
|
+
mode: 'observe' | 'enforce',
|
|
34
|
+
detail: { limit?, occupied?, focusId?, focusName?, evidence? } }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The reasons, and what the dashboard says for each:
|
|
38
|
+
|
|
39
|
+
| Reason | Dashboard | Means |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `product_limit_reached` | Held by tower: product limit reached (N of L running) | The product's free slots went to foci ahead of this one. |
|
|
42
|
+
| `waits_on` | Held by tower: waits on <focus> | A `depends_on` predecessor has not landed (tower graph). |
|
|
43
|
+
| `excluded_by` | Held by tower: excluded by <focus> on <evidence> | An `excludes` edge joins it to a focus that is running or cleared ahead of it (tower graph). |
|
|
44
|
+
|
|
45
|
+
## The graph
|
|
46
|
+
|
|
47
|
+
Edges join two foci of ONE product (an edge across products, or from a focus to
|
|
48
|
+
itself, is refused):
|
|
49
|
+
|
|
50
|
+
- **`depends_on`** (directed): `toFocusId` waits until `fromFocusId` has
|
|
51
|
+
LANDED. A `depends_on` edge that would close a cycle is refused at the write,
|
|
52
|
+
and the refusal names the cycle (`UI -> Schema -> API -> UI`). The refusal is
|
|
53
|
+
atomic: a database trigger serializes each product's `depends_on` writes, so
|
|
54
|
+
two concurrent writes can never close a cycle between them.
|
|
55
|
+
- **`excludes`** (mutual): the two foci are never cleared together -- e.g. both
|
|
56
|
+
rewrite the same files, fixtures or migration journal. Stored once per pair.
|
|
57
|
+
|
|
58
|
+
Every edge carries **provenance** (`declared` -- written by a human or an agent
|
|
59
|
+
over MCP; `inferred` and `observed` are reserved for the follow-on focus that
|
|
60
|
+
draws edges itself), **evidence** (why the edge holds -- required), and who
|
|
61
|
+
wrote it and when. Deleting an edge leaves a tombstone (`deleted_at`).
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
telora_product_tower { action: "edge_create", productId, fromFocusId, toFocusId,
|
|
65
|
+
kind: "depends_on" | "excludes", evidence }
|
|
66
|
+
telora_product_tower { action: "edge_list", productId, focusId? }
|
|
67
|
+
telora_product_tower { action: "edge_delete", towerEdgeId }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## The landed rule
|
|
71
|
+
|
|
72
|
+
A focus has LANDED -- judged by the daemon, no human close -- when:
|
|
73
|
+
|
|
74
|
+
1. it has at least one delivery, and
|
|
75
|
+
2. every non-cancelled delivery is `done` (at least one: a focus whose every
|
|
76
|
+
delivery was cancelled landed nothing), and
|
|
77
|
+
3. its branch tip is contained in the integration branch
|
|
78
|
+
(`git rev-list --count <integration>..<branch>` reads 0; no checkout) -- or
|
|
79
|
+
the branch is gone AND the focus's merge record (`focus_merges`, written by
|
|
80
|
+
the daemon when it merges) names a merge commit integration contains. The
|
|
81
|
+
daemon removes a focus branch only after merging it; the record is the proof.
|
|
82
|
+
A branch that is gone with no such record (a hand-deleted branch, a legacy
|
|
83
|
+
focus that never recorded one) is unproven: unknown, never landed.
|
|
84
|
+
|
|
85
|
+
The PRD frontier's completion signal is this same rule (`prd/controller-shared.ts`
|
|
86
|
+
`computeCompletedFocusIds`): a frontier overcome clears only when its focus has
|
|
87
|
+
landed. A landed check git cannot answer (integration unreadable, a ref or
|
|
88
|
+
containment read that errored, an unreadable or missing merge record) is UNKNOWN: the tower leaves the dependent focus's verdict unavailable (fail
|
|
89
|
+
open) rather than holding it, and the PRD frontier waits a tick.
|
|
90
|
+
|
|
91
|
+
## Precedence
|
|
92
|
+
|
|
93
|
+
A verdict records exactly one reason. When several apply, the first in this
|
|
94
|
+
order wins (daemon-core `TOWER_HOLD_REASONS`):
|
|
95
|
+
|
|
96
|
+
1. `waits_on` -- a fact about this focus's prerequisites;
|
|
97
|
+
2. `excluded_by` -- a fact about this focus's collisions;
|
|
98
|
+
3. `product_limit_reached` -- a fact about the queue.
|
|
99
|
+
|
|
100
|
+
A focus held for a graph reason takes no slot. `excluded_by` holds the focus
|
|
101
|
+
reached second, in rank order, while the other runs or is cleared, so two
|
|
102
|
+
excluded foci are never cleared together.
|
|
103
|
+
|
|
104
|
+
## The slot rule
|
|
105
|
+
|
|
106
|
+
A focus occupies one of the product's slots **only while it has a live coding
|
|
107
|
+
or review team**. An armed focus with no live team -- waiting on CI, a verify
|
|
108
|
+
hold, an escalation -- occupies nothing, so one stalled focus never holds the
|
|
109
|
+
product's queue.
|
|
110
|
+
|
|
111
|
+
Each pass, per product:
|
|
112
|
+
|
|
113
|
+
1. Every focus with a live team is cleared (the tower never stops a running team).
|
|
114
|
+
2. Free slots = limit - live foci (unbounded with no limit).
|
|
115
|
+
3. The other armed foci are walked in **focus rank order** (lowest
|
|
116
|
+
`priority_rank` first) -- foci the spawn passes last found wanting a team
|
|
117
|
+
ahead of foci they found idle. A focus that waits on an unlanded
|
|
118
|
+
predecessor, or is excluded by a focus already running or cleared, holds
|
|
119
|
+
for that reason; the rest each take a free slot (cleared) until none is
|
|
120
|
+
left, and the remainder hold `product_limit_reached`.
|
|
121
|
+
|
|
122
|
+
"Wanting" is CURRENT demand: a focus wants a slot when it could run now if the
|
|
123
|
+
tower and the host cap let it. Before the tower allocates, every ready-focus
|
|
124
|
+
pass refreshes stale agent roles (as the spawn pass does) and probes each
|
|
125
|
+
surfaced focus -- ready, or holding a queued directive -- through the gates its
|
|
126
|
+
spawn pass applies, in the pass's order, bar the tower and the host cap
|
|
127
|
+
(`packages/daemon/src/focus/spawn-demand-probe.ts`): its role, a live team, the
|
|
128
|
+
review budget, an open parking escalation (the guard `spawnFocusTeam` applies),
|
|
129
|
+
a demoted just-torn-down respawn, a queued review whose window cannot be read
|
|
130
|
+
(the ready pass withholds it), the operator-hold park, a review at an approved
|
|
131
|
+
head, a verify hold, a cooldown, no team work, and -- for a queued directive --
|
|
132
|
+
an active row the pass could not read (it cannot spawn without one). So a focus held by any of
|
|
133
|
+
those reserves no slot, even the first time it surfaces; a focus whose gate
|
|
134
|
+
clears is ranked with the waiters on that same pass; a focus that spawned has
|
|
135
|
+
had its demand met; and a focus no longer surfaced wants nothing -- when a team
|
|
136
|
+
exits, its focus reserves nothing and the slot goes to the next wanting focus on
|
|
137
|
+
the same pass. The gates that dispose of, park or release a queued directive (a
|
|
138
|
+
stale stage, a closed review window, a superseded coding directive, the
|
|
139
|
+
operator-hold park and its release, a review at an approved head) run for real
|
|
140
|
+
before allocation: they are idempotent and the pending pass would make the same
|
|
141
|
+
disposal on that poll. A probe that cannot answer keeps a fresh record and
|
|
142
|
+
otherwise fails open (the focus wants a slot), so a read error never demotes a
|
|
143
|
+
higher-ranked focus behind the waiters.
|
|
144
|
+
|
|
145
|
+
The pass runs at the head of every ready-focus pass -- on daemon start, on
|
|
146
|
+
every 30s poll, on the engine's 60s timer and on a completion trigger; there is
|
|
147
|
+
no production path to the ready pass that skips it, and the passes never overlap
|
|
148
|
+
(`runReadyFocusPassThroughTower`, pinned by `tower/tower-entrypoints.test.ts`) -- so a change to the armed set, the limit, focus rank or a
|
|
149
|
+
focus's team presence is reflected within one poll. It writes only a verdict
|
|
150
|
+
that changed, or one older than half the freshness bound (5 minutes), so a
|
|
151
|
+
running tower's verdicts never go stale. It also clears the verdict of a focus
|
|
152
|
+
that is no longer armed. A pass that throws for one product is logged and never
|
|
153
|
+
stops another product's pass; admission fails open on that product's foci at
|
|
154
|
+
once (see the fail-open rule).
|
|
155
|
+
|
|
156
|
+
## The three modes: `TELORA_TOWER_MODE`
|
|
157
|
+
|
|
158
|
+
Declared in `@telora/daemon-core` `env-config.ts`; default **`observe`**.
|
|
159
|
+
|
|
160
|
+
| Mode | Tower pass | Spawn passes |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `off` | Does not run. | Exactly as before the tower: API order, host cap only. |
|
|
163
|
+
| `observe` | Writes verdicts; the dashboard shows them, marked "(observe only)". | Ignore verdicts. Every spawn decision `enforce` would have made differently is logged: `[tower] observe: enforce would hold "<focus>" (<reason> since <t>); decision taken: spawned`. |
|
|
164
|
+
| `enforce` | Writes verdicts. | Start no coding or review team for a focus whose verdict is holding AND fresh; offer ready foci, and foci with a queued (e.g. review) directive, to the host in focus rank order. |
|
|
165
|
+
|
|
166
|
+
## The fail-open rule
|
|
167
|
+
|
|
168
|
+
In every mode, only a fresh, explicit holding verdict ever stops a spawn, and
|
|
169
|
+
only while this daemon's tower pass for the focus's product is STANDING: its
|
|
170
|
+
most recent pass for the product succeeded, within the freshness bound. When the
|
|
171
|
+
product's pass fails -- or has not yet succeeded since the daemon started --
|
|
172
|
+
every verdict of that product is unobeyed AT ONCE, however fresh, and the daemon
|
|
173
|
+
logs `[tower] "<focus>": its product's tower pass failed -- failing open`.
|
|
174
|
+
Another product's holds are untouched. A verdict that is
|
|
175
|
+
|
|
176
|
+
- missing,
|
|
177
|
+
- unreadable,
|
|
178
|
+
- older than the bounded age (`TOWER_VERDICT_MAX_AGE_MS`, 5 minutes), or
|
|
179
|
+
- computed under an earlier arm (its `armedAt` is not the focus's current `agent_assigned_at`)
|
|
180
|
+
|
|
181
|
+
is NO verdict: the focus spawns exactly as it would with the tower off, the
|
|
182
|
+
daemon logs `[tower] "<focus>": tower verdict unavailable (<why>) -- failing
|
|
183
|
+
open`, and the dashboard names it ("Tower verdict unavailable (stale) --
|
|
184
|
+
spawns as with the tower off"). A tower that stops writing therefore releases
|
|
185
|
+
every hold within five minutes rather than freezing the product.
|
|
186
|
+
|
|
187
|
+
The dashboard judges freshness on a clock while the page is open, so a hold
|
|
188
|
+
turns into "unavailable" on screen when the spawn pass stops obeying it. Whether
|
|
189
|
+
the product's tower is running at all comes from the daemon heartbeat
|
|
190
|
+
(`metadata.tower` = `{ [productId]: { mode, pass } }`): with the tower running,
|
|
191
|
+
an armed focus with no verdict reads "unavailable" even when no focus has one,
|
|
192
|
+
and a failed pass marks every armed focus of the product "unavailable (the
|
|
193
|
+
product's tower pass failed)"; with `TELORA_TOWER_MODE=off` no tower line shows.
|
|
194
|
+
|
|
195
|
+
Why admission fails OPEN while the merge gates fail CLOSED: the failures are not
|
|
196
|
+
symmetric. A wrongly blocked merge is visible -- a held delivery, a red gate, a
|
|
197
|
+
named reason. A wrongly held spawn is silent -- a focus that simply never runs,
|
|
198
|
+
which is the daemon's recurring bug class. The tower is in front of every
|
|
199
|
+
spawn, so it must never be able to cause that silently.
|
|
200
|
+
|
|
201
|
+
## Moving a host from observe to enforce
|
|
202
|
+
|
|
203
|
+
1. Leave the host in `observe` (the default) and set the product's limit:
|
|
204
|
+
`telora_product update { productId, towerConcurrencyLimit: 1 }`.
|
|
205
|
+
2. Arm the foci. On the dashboard each armed focus now carries the tower's
|
|
206
|
+
line; on the host, read the divergences:
|
|
207
|
+
`telora-daemon logs | grep '\[tower\]'`.
|
|
208
|
+
3. Judge them on real foci: every `observe: enforce would hold` line should be
|
|
209
|
+
a spawn you would have wanted held, and no focus should show "tower verdict
|
|
210
|
+
unavailable" while the daemon is healthy.
|
|
211
|
+
4. Set `TELORA_TOWER_MODE=enforce` in the daemon's environment and restart it
|
|
212
|
+
(drain first if teams are running). Holds now take effect on the next poll.
|
|
213
|
+
5. To back out: `TELORA_TOWER_MODE=observe` (verdicts kept, not obeyed) or
|
|
214
|
+
`off` (no pass at all) and restart. Clearing the product limit
|
|
215
|
+
(`towerConcurrencyLimit: null`) releases every `product_limit_reached`
|
|
216
|
+
hold on the next poll without a restart.
|
|
217
|
+
|
|
218
|
+
## Reading a stuck focus
|
|
219
|
+
|
|
220
|
+
- **"Held by tower: product limit reached"** -- look at which foci are running
|
|
221
|
+
(they hold the slots) and their rank; raise the limit or re-rank if the
|
|
222
|
+
order is wrong.
|
|
223
|
+
- **"Held by tower: waits on <focus>"** -- the predecessor has not landed: it
|
|
224
|
+
still has open deliveries, or its branch is not yet merged into integration.
|
|
225
|
+
If the dependency is wrong, delete the edge (`edge_delete`).
|
|
226
|
+
- **"Held by tower: excluded by <focus> on <evidence>"** -- the other focus is
|
|
227
|
+
running (or was cleared ahead of this one); this one clears when it stops.
|
|
228
|
+
- **"Tower verdict unavailable"** -- the focus will spawn anyway. Check the
|
|
229
|
+
daemon log for `[tower] pass failed for product ...`.
|
|
230
|
+
- **"Waiting for a session slot"** -- the tower has cleared it; the HOST cap is
|
|
231
|
+
full. That is the session cap, not the tower.
|
|
232
|
+
|
|
233
|
+
## Stated limit
|
|
234
|
+
|
|
235
|
+
A product served by two daemon hosts would get two towers writing the same
|
|
236
|
+
verdicts, each counting only its own live teams. No product is configured that
|
|
237
|
+
way.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telora/daemon",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.8",
|
|
4
4
|
"description": "Agent orchestration daemon for Telora - spawns and manages Claude Code instances",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"scripts/telora-daemon-wrapper.sh",
|
|
21
21
|
"build-info.json",
|
|
22
22
|
"README.md",
|
|
23
|
-
".env.example"
|
|
23
|
+
".env.example",
|
|
24
|
+
"docs/runbook-tower.md"
|
|
24
25
|
],
|
|
25
26
|
"scripts": {
|
|
26
27
|
"dev": "tsx watch src/index.ts",
|
|
@@ -33,12 +34,14 @@
|
|
|
33
34
|
"test": "node scripts/run-tests.mjs",
|
|
34
35
|
"stamp-build-info": "node scripts/stamp-build-info.mjs",
|
|
35
36
|
"stamp-build-info:local": "node scripts/stamp-build-info.mjs --empty",
|
|
36
|
-
"
|
|
37
|
+
"copy-shipped-docs": "node scripts/copy-shipped-docs.mjs",
|
|
38
|
+
"prepublishOnly": "npm run build && npm run stamp-build-info",
|
|
39
|
+
"prepack": "npm run copy-shipped-docs"
|
|
37
40
|
},
|
|
38
41
|
"//testRunner": "Uses Node.js built-in test runner (node:test) via tsx for TypeScript support. The root package uses Vitest; this is a deliberate choice for the daemon package to avoid framework dependencies. Test files live beside the modules they exercise (owner-directory layout); the runner discovers them recursively.",
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"@telora/daemon-core": "^0.3.
|
|
41
|
-
"@telora/mcp-products": "^0.23.
|
|
43
|
+
"@telora/daemon-core": "^0.3.1",
|
|
44
|
+
"@telora/mcp-products": "^0.23.5",
|
|
42
45
|
"commander": "^14.0.3",
|
|
43
46
|
"yaml": "^2.4.0",
|
|
44
47
|
"zod": "^4.3.6"
|