pantheon-opencode 1.6.0-beta.1 → 1.6.0-beta.2

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/CHANGELOG.md CHANGED
@@ -18,6 +18,48 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
18
18
 
19
19
  ## ✅ Closed Issues
20
20
 
21
+ ## [v1.6.0-beta.2] - 2026-10-02
22
+
23
+ ## 🆕 What's New
24
+ - **`steps` não é mais definido pelo Pantheon**: removido dos 14 agentes em
25
+ `src/agents/`. No OpenCode 2 o campo é opcional e **sem default** — ausente
26
+ significa **sem teto nativo**. O host não impõe teto; o Pantheon estava
27
+ escrevendo um.
28
+ - **Instalador v2 remove `steps` residual**: configs já instalados passam a ter
29
+ o `steps` apagado dos agentes gerenciados. Escopo restrito aos agentes que o
30
+ Pantheon gerencia — um agente definido pelo usuário nunca é tocado. O bloco V1
31
+ singular `agent` é deliberadamente intocado (V1 em retirada).
32
+ - **Substitutos ao teto**: `task(background=true)` para trabalho fora da
33
+ foreground, `experimental.subagent_depth` para aninhamento de subagentes e
34
+ paralelismo entre delegações independentes.
35
+ - **Limpeza**: `max_steps` e `max_compaction_items` removidos de
36
+ `src/routing.yml`. `loadRoutingMaxSteps` nunca teve um único import — 2
37
+ matches no repo (definição e typedef), ambos removidos.
38
+
39
+ ## 🐞 Fixed
40
+ - **`migrateV1toV2` descartava campos gerenciados**: com os blocos `agent` (V1)
41
+ e `agents` (V2) coexistindo — o caso de qualquer config já instalado — o
42
+ spread raso fazia um substituir o outro por inteiro. Na ordem de chaves do
43
+ `opencode.json` real, o lado perdedor era o do usuário. Agora **mescla**: a
44
+ base é o bloco V2, os campos gerenciados do V1 sobrescrevem, e os campos do
45
+ usuário são preservados. Mesma semântica já usada em `install/opencode.mjs`.
46
+ - **`docs/platforms/README.md` tinha tabela de step limits errada**: 14 linhas
47
+ listando limites por agente, **nenhum valor batendo** com `src/agents/`.
48
+ Tabela removida e a seção reescrita.
49
+
50
+ ## ⚠️ Known Issues
51
+ - **Permissões de agente**: V1 usa `permission` e V2 usa `permissions`; ao mesclar esses blocos, a chave legada `permission` pode permanecer inerte ao lado de `permissions`.
52
+ - **Migração — rode o init uma vez**: `npx pantheon-opencode init
53
+ --opencode-version=v2` **uma única vez** limpa o `steps` residual. Sem isso o
54
+ config instalado continua carregando o teto antigo.
55
+ - **O `.md` de agente instalado é sobrescrito no próximo sync**: editar
56
+ `~/.config/opencode/agents/*.md` esperando persistência **não funciona**.
57
+ Comportamento **pré-existente**, não é regressão desta mudança — mas agora
58
+ está documentado para não ser reportado como uma.
59
+ - **`steps` definido à mão num agente gerenciado é removido a cada install v2**:
60
+ o instalador não distingue um valor que ele escreveu de um valor que você
61
+ escreveu (mesma chave, mesmo valor). Workaround: definir no frontmatter do
62
+ agente — e lembrar que ele **também** é sobrescrito no próximo sync.
21
63
  ## [v1.6.0-beta.1] - 2026-09-30
22
64
 
23
65
  <!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
package/README.md CHANGED
@@ -78,11 +78,14 @@ clear handoff between stages of work.
78
78
 
79
79
  ## Status
80
80
 
81
- Operational checkout version: **v1.6.0-beta.1** (first beta compatible with
82
- OpenCode 2; publication is not asserted here). Pantheon is designed for OpenCode and depends on the
83
- availability and configuration of OpenCode and any optional services you choose
84
- to use. Check the [releases](https://github.com/ils15/pantheon-opencode/releases)
85
- and [changelog](CHANGELOG.md) for the latest published changes.
81
+ Candidate version: the manifests in this checkout express the version being
82
+ prepared, not a confirmed publication. A beta is published only after the
83
+ Release workflow completes successfully. To check which version is actually
84
+ published, consult the npm `beta` dist-tag. Pantheon is designed for OpenCode
85
+ and depends on the availability and configuration of OpenCode and any optional
86
+ services you choose to use. Check the
87
+ [releases](https://github.com/ils15/pantheon-opencode/releases) and
88
+ [changelog](CHANGELOG.md) for the latest published changes.
86
89
 
87
90
  ## Delegation (native `task()`)
88
91
 
@@ -231,6 +234,29 @@ The installer still writes the compatibility settings required by the selected
231
234
  OpenCode host, such as `experimental.subagent_depth`; this does not convert a
232
235
  V1 plugin into V2 or provide V2 with V1 hooks.
233
236
 
237
+ Pantheon does not set a step ceiling on agents. On OpenCode 2 the `steps`
238
+ field is optional and has no default: absent means the host applies no native
239
+ limit. A config written before this change still carries the old value, and the
240
+ V2 install path strips it from every agent Pantheon manages — an agent you
241
+ define yourself is never touched, and the retiring V1 singular `agent` block
242
+ is left alone. Run the installer once to clear a stale value from an existing
243
+ config:
244
+
245
+ ```bash
246
+ npx pantheon-opencode init --opencode-version=v2
247
+ ```
248
+
249
+ Context control is not a Pantheon setting. Whatever budget an agent runs
250
+ against comes from the compaction settings of the OpenCode host you are
251
+ running, and Pantheon does not define them.
252
+
253
+ When a V1 `agent` block and a V2 `agents` block are both present — which is
254
+ what an installed `opencode.json` carries — the V1→V2 conversion merges them
255
+ instead of letting one replace the other. The V2 block is the base, the
256
+ framework-managed fields from the V1 block overwrite, and your own fields are
257
+ preserved, matching the precedence the installer already applies elsewhere.
258
+ Key order does not matter.
259
+
234
260
  ## Updating between releases (beta.5+)
235
261
 
236
262
  One command keeps an existing installation current:
package/README.pt-BR.md CHANGED
@@ -171,6 +171,29 @@ O instalador continua gravando as configurações de compatibilidade exigidas
171
171
  pelo host OpenCode selecionado, como `experimental.subagent_depth`; isso não
172
172
  converte um plugin V1 em V2 nem dá hooks V1 ao V2.
173
173
 
174
+ O Pantheon não define mais um teto de passos para os agentes. No OpenCode 2 o
175
+ campo `steps` é opcional e não tem default: ausente significa que o host não
176
+ aplica nenhum limite nativo. Uma config escrita antes dessa mudança ainda
177
+ carrega o valor antigo, e o caminho de instalação v2 o remove de todos os
178
+ agentes que o Pantheon gerencia — um agente definido por você nunca é tocado,
179
+ e o bloco V1 singular `agent`, em retirada, é deixado intacto. Rode o
180
+ instalador uma vez para limpar um valor antigo de uma config existente:
181
+
182
+ ```bash
183
+ npx pantheon-opencode init --opencode-version=v2
184
+ ```
185
+
186
+ O controle de contexto não é uma configuração do Pantheon. Qualquer orçamento
187
+ que um agente terá vem das settings de compactação do próprio host OpenCode,
188
+ e o Pantheon não as define.
189
+
190
+ Quando um bloco V1 `agent` e um bloco V2 `agents` coexistem — o que acontece
191
+ em qualquer `opencode.json` já instalado — a conversão V1→V2 mescla os dois em
192
+ vez de deixar um substituir o outro. O bloco V2 é a base, os campos
193
+ gerenciados que vêm do V1 sobrescrevem, e os seus próprios campos são
194
+ preservados, com a mesma precedência que o instalador já aplica em outros
195
+ pontos. A ordem das chaves não muda o resultado.
196
+
174
197
  ## Atualizando entre releases (beta.5+)
175
198
 
176
199
  Um comando mantém uma instalação existente em dia:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantheon-opencode",
3
- "version": "1.6.0-beta.1",
3
+ "version": "1.6.0-beta.2",
4
4
  "description": "Multi-agent orchestration for OpenCode — 14 specialized agents, TDD, Themis review gates.",
5
5
  "type": "module",
6
6
  "workspaces": [
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon",
3
3
  "publisher": "ils15",
4
- "version": "1.6.0-beta.1",
4
+ "version": "1.6.0-beta.2",
5
5
  "displayName": "Pantheon",
6
6
  "description": "Conductor-Delegate orchestration platform with 14 specialized agents, 21 skills, and OpenCode support.",
7
7
  "agents": "./src/agents",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
4
4
 
5
5
  [project]
6
6
  name = "pantheon"
7
- version = "1.6.0-beta.1"
7
+ version = "1.6.0-beta.2"
8
8
  description = "Multi-agent orchestration framework"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []
@@ -304,6 +304,114 @@ const V2_TO_V1_RENAMES = Object.fromEntries(
304
304
  Object.entries(V1_TO_V2_RENAMES).map(([k, v]) => [v, k]),
305
305
  )
306
306
 
307
+ /**
308
+ * Agent fields owned by the framework. Mirrors the exported MANAGED_FIELDS in
309
+ * install/opencode.mjs plus `source`, which the installer writes next to them.
310
+ * The migration runs before that merge, so it has to apply the same precedence
311
+ * itself when both `agent` and `agents` coexist: these come from the V1 block,
312
+ * every other field stays the user's.
313
+ *
314
+ * `permissions` is the V2 name — the converter renames the V1 `permission`
315
+ * block before the merge runs.
316
+ *
317
+ * `description` is deliberately absent, and this list is deliberately narrower
318
+ * than what the installer writes. The installer writes a description only when
319
+ * it creates an agent (install/opencode.mjs, the new-agent branch); on an agent
320
+ * that already exists it leaves the value alone, under its own comment "Existing
321
+ * agent fields belong to the user". So a description is framework-owned for a
322
+ * NEW agent and user-owned for an EXISTING one. Listing it here would make the
323
+ * migration stricter than the installer and would overwrite a description the
324
+ * installer deliberately preserves. mergeManagedAgents carries the value across
325
+ * instead, so it survives the merge without being treated as framework-owned.
326
+ *
327
+ * KNOWN DEBT: the installer still writes the V1 spelling `permission`, while V2
328
+ * reads `permissions`. An agent that goes through this merge can therefore end
329
+ * up carrying both keys, with `permission` left as dead weight that nothing
330
+ * reads. Tracked for the next slice; not fixed here, because picking a winner
331
+ * changes what an install writes.
332
+ */
333
+ export const MANAGED_AGENT_FIELDS = Object.freeze([
334
+ 'source',
335
+ 'temperature',
336
+ 'color',
337
+ 'permissions',
338
+ 'mode',
339
+ 'hidden',
340
+ 'disable_model_invocation',
341
+ ])
342
+
343
+ /**
344
+ * Compare this module's managed-agent list against the installer's.
345
+ *
346
+ * The two merge paths have to agree: the migration reimplements the installer's
347
+ * merge because it runs before it. A field present on only one side is silent
348
+ * divergence — either the migration overwrites a field the installer leaves
349
+ * alone, or it stops mirroring one the installer now writes. Both directions
350
+ * matter, so the result is reported as a pair of differences rather than a
351
+ * boolean.
352
+ *
353
+ * Names are compared after the converter's renames, because that is the
354
+ * spelling the merge actually sees.
355
+ *
356
+ * @param {string[]} installerFields - MANAGED_FIELDS as exported by install/opencode.mjs
357
+ * @returns {{ onlyInMigration: string[], onlyInInstaller: string[] }}
358
+ */
359
+ export function managedAgentFieldDrift(installerFields) {
360
+ const toV2Name = (field) => V1_TO_V2_RENAMES[field] ?? field
361
+ const migration = new Set(MANAGED_AGENT_FIELDS.map(toV2Name))
362
+ const installer = new Set(installerFields.map(toV2Name))
363
+ return {
364
+ onlyInMigration: [...migration].filter((f) => !installer.has(f)).sort(),
365
+ onlyInInstaller: [...installer].filter((f) => !migration.has(f)).sort(),
366
+ }
367
+ }
368
+
369
+ /** @returns {boolean} true for a plain name→config map (not null/array) */
370
+ function isAgentMap(value) {
371
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
372
+ }
373
+
374
+ /**
375
+ * Merge the converted V1 `agent` block into an existing V2 `agents` base.
376
+ *
377
+ * Agents present in both are merged field by field: the base is kept, keys the
378
+ * base does not have are filled in from the V1 block, and MANAGED_AGENT_FIELDS
379
+ * are overwritten by the V1 values. Agents present in only one block are taken
380
+ * as they are — a union, not a replacement.
381
+ *
382
+ * Filling the gaps matters, and it is not description-specific: any key present
383
+ * only in the V1 block used to vanish here — `model`, `provider` and `mcp` too.
384
+ * `description` is the visible case, because the installer never writes one for
385
+ * an existing agent, so the V1 block is the only place that value can come from
386
+ * and the agent lost it on every install, permanently. Filling gaps can never
387
+ * destroy a base value; on conflict the base still wins, as before.
388
+ *
389
+ * @param {object} base - existing V2 `agents` map
390
+ * @param {object} managed - converted V1 `agent` map
391
+ * @returns {object} merged map
392
+ */
393
+ function mergeManagedAgents(base, managed) {
394
+ const merged = { ...base }
395
+
396
+ for (const [name, agentConfig] of Object.entries(managed)) {
397
+ const existing = merged[name]
398
+ if (!isAgentMap(existing) || !isAgentMap(agentConfig)) {
399
+ merged[name] = agentConfig
400
+ continue
401
+ }
402
+ const entry = { ...existing }
403
+ for (const [field, value] of Object.entries(agentConfig)) {
404
+ if (!(field in entry)) entry[field] = value
405
+ }
406
+ for (const field of MANAGED_AGENT_FIELDS) {
407
+ if (field in agentConfig) entry[field] = agentConfig[field]
408
+ }
409
+ merged[name] = entry
410
+ }
411
+
412
+ return merged
413
+ }
414
+
307
415
  // ---------------------------------------------------------------------------
308
416
  // Public API
309
417
  // ---------------------------------------------------------------------------
@@ -333,7 +441,20 @@ export function migrateV1toV2(config) {
333
441
  }
334
442
  agents[agentName] = ac
335
443
  }
336
- result.agents = agents
444
+ // An `agents` (V2) block may already exist — the installed opencode.json
445
+ // carries both, in either key order. Renaming onto it with a plain
446
+ // replace dropped the whole managed merge, so merge instead.
447
+ const base = result.agents
448
+ result.agents = isAgentMap(base) ? mergeManagedAgents(base, agents) : agents
449
+ } else if (key === 'agents') {
450
+ // V2 block: it is the base. The singular `agent` key may already have
451
+ // been converted above, in which case merge the two.
452
+ const managed = result.agents
453
+ if (isAgentMap(value)) {
454
+ result.agents = isAgentMap(managed) ? mergeManagedAgents(value, managed) : value
455
+ } else if (!isAgentMap(managed)) {
456
+ result.agents = value
457
+ }
337
458
  } else if (key === 'provider' && typeof value === 'object' && value !== null) {
338
459
  const providers = {}
339
460
  for (const [provName, provConfig] of Object.entries(value)) {
@@ -61,6 +61,33 @@ const COMPONENT_NAMES = [
61
61
  'runtime',
62
62
  ]
63
63
 
64
+ /**
65
+ * Per-agent fields owned by the framework: for an agent that already exists in
66
+ * the target config these are overwritten from the canonical source, and for a
67
+ * new agent they are the seed. Everything else on an existing entry is the
68
+ * user's and is left alone.
69
+ *
70
+ * `source` is written right next to these (for both new and existing agents) but
71
+ * is kept out of the loop so it can also be set independently — the migration in
72
+ * config-migration.mjs mirrors this list plus `source`, and tests assert the two
73
+ * stay equal as sets.
74
+ *
75
+ * KNOWN DEBT: `permission` is the V1 spelling. V2 reads `permissions`, so an
76
+ * agent can carry both keys after an install and the V1 one becomes dead weight.
77
+ * Tracked for the next slice; changing it here would change what an install
78
+ * writes.
79
+ *
80
+ * @type {readonly string[]}
81
+ */
82
+ export const MANAGED_FIELDS = Object.freeze([
83
+ 'temperature',
84
+ 'color',
85
+ 'permission',
86
+ 'mode',
87
+ 'hidden',
88
+ 'disable_model_invocation',
89
+ ])
90
+
64
91
  function deepClone(value) {
65
92
  return JSON.parse(JSON.stringify(value))
66
93
  }
@@ -836,7 +863,6 @@ export async function installOpenCode(
836
863
  if (fm.mode) agent.mode = fm.mode
837
864
  if (fm.hidden) agent.hidden = fm.hidden
838
865
  if (fm.temperature !== undefined) agent.temperature = fm.temperature
839
- if (fm.steps !== undefined) agent.steps = fm.steps
840
866
  // Support both hyphen (YAML) and underscore (JSON) key variants
841
867
  if (fm['disable-model-invocation'] !== undefined)
842
868
  agent.disable_model_invocation = fm['disable-model-invocation']
@@ -855,15 +881,6 @@ export async function installOpenCode(
855
881
 
856
882
  const canonicalAgentConfig = readAgentConfigFromCanonical()
857
883
  const agentSources = getAgentSources(agentPrefix)
858
- const MANAGED_FIELDS = [
859
- 'steps',
860
- 'temperature',
861
- 'color',
862
- 'permission',
863
- 'mode',
864
- 'hidden',
865
- 'disable_model_invocation',
866
- ]
867
884
 
868
885
  if (config.agent === undefined) config.agent = {}
869
886
 
@@ -1222,6 +1239,22 @@ export async function installOpenCode(
1222
1239
  if (version === 'v2') {
1223
1240
  info(S.v2Migrating)
1224
1241
  configToWrite = migrateV1toV2(config)
1242
+
1243
+ // Pantheon no longer sets a step ceiling. Any `steps` sitting on a managed
1244
+ // agent was written by an earlier install and never chosen by the user —
1245
+ // the merge above is additive by design (no delete path), so drop it here
1246
+ // and let the host default apply. This runs after the migration because
1247
+ // migrateV1toV2 copies the V1 `agent` block across verbatim (including any
1248
+ // stale `steps`), which is exactly how the value reappears in V2.
1249
+ // User-defined agents are out of scope and keep their value; the V1
1250
+ // singular `agent` block is deliberately untouched (V1 is retiring).
1251
+ const v2Agents = configToWrite.agents
1252
+ if (v2Agents && typeof v2Agents === 'object' && !Array.isArray(v2Agents)) {
1253
+ for (const name of Object.keys(canonicalAgentConfig)) {
1254
+ const entry = v2Agents[name]
1255
+ if (entry && typeof entry === 'object' && !Array.isArray(entry)) delete entry.steps
1256
+ }
1257
+ }
1225
1258
  }
1226
1259
 
1227
1260
  const configContent = `${JSON.stringify(configToWrite, null, 2)}\n`
package/skills-lock.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "src/skills/agent-coordination/SKILL.md": "cfd040ef29e3dd0fd5e0e67773874facf7c06e3284898afe1fddba4456c3f1aa",
3
3
  "src/skills/artifact-management/SKILL.md": "c60e493666bedf46e9113d0dd20106489d5c5a73905e1370fcfc1c0d71c59185",
4
- "src/skills/auto-continue/SKILL.md": "f6ff530b5537af9c8a0224a52eb0da6da7f9cf4ab4958ce2908c5c3a911b8879",
4
+ "src/skills/auto-continue/SKILL.md": "4fe256d44ee9828d622a0d8b63cfc6bb39a317189a9ccf19494a6efd4527a7a9",
5
5
  "src/skills/clonedeps/SKILL.md": "3e0b43cf96da98c83699dc48db3fd58fbfda84b9022bb70d3747875c81c3e124",
6
6
  "src/skills/code-review-checklist/SKILL.md": "0e59367c65ef4608db80a0674279f32cfc3f67dca7236f233b61a47fe506579f",
7
7
  "src/skills/codemap/SKILL.md": "87af5271e1576e154cadff313eab16264b726e43d094520736b7a5b03b409a4a",
@@ -14,7 +14,6 @@ permission:
14
14
  task:
15
15
  "*": deny
16
16
  temperature: 0.3
17
- steps: 70
18
17
  skills:
19
18
  - tdd-with-agents
20
19
  - visual-review-pipeline
@@ -17,7 +17,6 @@ permission:
17
17
  task:
18
18
  "*": deny
19
19
  temperature: 0.1
20
- steps: 30
21
20
  skills:
22
21
  - auto-continue
23
22
  mcp_tools:
@@ -6,7 +6,6 @@ description: Strategic planner & architect — research-first, plan-only, never
6
6
  mode: all
7
7
  reasoning_effort: high
8
8
 
9
- steps: 30
10
9
  mcp_tools:
11
10
  pantheon-resources: all
12
11
  pantheon-memory: [memory_search]
@@ -12,7 +12,6 @@ permission:
12
12
  task:
13
13
  "*": deny
14
14
  temperature: 0.2
15
- steps: 60
16
15
  skills:
17
16
  - tdd-with-agents
18
17
  - incremental-implementation
@@ -16,7 +16,6 @@ permission:
16
16
  task:
17
17
  "*": deny
18
18
  temperature: 0.2
19
- steps: 30
20
19
  skills:
21
20
  - auto-continue
22
21
  mcp_tools:
@@ -12,7 +12,6 @@ permission:
12
12
  task:
13
13
  "*": deny
14
14
  temperature: 0.3
15
- steps: 60
16
15
  skills:
17
16
  - tdd-with-agents
18
17
  - auto-continue
@@ -11,7 +11,6 @@ mcp_tools:
11
11
  pantheon-memory: [memory_search]
12
12
  pantheon-code-mode: [execute_code_script]
13
13
  temperature: 0.3
14
- steps: 70
15
14
  skills:
16
15
  - tdd-with-agents
17
16
  - file-prompts
@@ -14,7 +14,6 @@ permission:
14
14
  task:
15
15
  "*": deny
16
16
  temperature: 0.2
17
- steps: 25
18
17
  skills:
19
18
  - git-workflow-and-versioning
20
19
  - artifact-management
@@ -6,7 +6,6 @@ description: Memory bank quality owner — initializes .pantheon/memory-bank/, w
6
6
  mode: all
7
7
  reasoning_effort: low
8
8
 
9
- steps: 25
10
9
  mcp_tools:
11
10
  pantheon-resources: all
12
11
  pantheon-memory:
package/src/agents/nyx.md CHANGED
@@ -12,7 +12,6 @@ permission:
12
12
  task:
13
13
  "*": deny
14
14
  temperature: 0.1
15
- steps: 25
16
15
  skills:
17
16
  - security-hardening
18
17
  - auto-continue
@@ -12,7 +12,6 @@ permission:
12
12
  task:
13
13
  "*": deny
14
14
  temperature: 0.2
15
- steps: 30
16
15
  skills:
17
16
  - git-workflow-and-versioning
18
17
  - incremental-implementation
@@ -10,7 +10,6 @@ permission:
10
10
  task:
11
11
  "*": deny
12
12
  temperature: 0.3
13
- steps: 35
14
13
  skills:
15
14
  - incremental-implementation
16
15
  mcp_tools:
@@ -11,7 +11,6 @@ permission:
11
11
  task:
12
12
  "*": deny
13
13
  temperature: 0.1
14
- steps: 30
15
14
  skills:
16
15
  - code-review-checklist
17
16
  - security-hardening
@@ -9,7 +9,6 @@ permission:
9
9
  task:
10
10
  "*": allow
11
11
  temperature: 0.2
12
- steps: 45
13
12
  mcp_tools:
14
13
  pantheon-resources: all
15
14
  pantheon-memory:
@@ -104,14 +104,6 @@ export interface RoutingRetryPolicyOptions {
104
104
  logger?: { warn?: (msg: string) => void }
105
105
  }
106
106
 
107
- /**
108
- * R4: per-agent step caps from routing.yml `agents.<name>.max_steps`.
109
- * Fail-open → {} (no agent capped).
110
- */
111
- export declare function loadRoutingMaxSteps(
112
- options?: RoutingRetryPolicyOptions,
113
- ): Record<string, number>
114
-
115
107
  /**
116
108
  * O5: permission.task glob rules from routing.yml `permission.task`
117
109
  * (glob pattern → 'allow' | 'deny'). Fail-open → null.
@@ -213,35 +213,6 @@ function loadRoutingYaml(routingPath) {
213
213
  return routing && typeof routing === 'object' ? routing : null
214
214
  }
215
215
 
216
- /**
217
- * Load the R4 per-agent step caps from routing.yml
218
- * (`agents.<name>.max_steps`). Fail-open: missing config yields {}.
219
- *
220
- * @param {object} [opts]
221
- * @param {string} [opts.routingPath]
222
- * @param {{warn?: Function}} [opts.logger]
223
- * @returns {Record<string, number>} lowercase agent → max_steps
224
- */
225
- export function loadRoutingMaxSteps({ routingPath, logger = console } = {}) {
226
- try {
227
- const routing = loadRoutingYaml(routingPath)
228
- const agents = routing?.agents
229
- if (!agents || typeof agents !== 'object') return {}
230
- const out = {}
231
- for (const [agent, spec] of Object.entries(agents)) {
232
- const maxSteps = spec && typeof spec === 'object' ? spec.max_steps : undefined
233
- if (Number.isSafeInteger(maxSteps) && maxSteps > 0) {
234
- out[agent.toLowerCase()] = maxSteps
235
- }
236
- }
237
- return out
238
- } catch (err) {
239
- const reason = err instanceof Error ? err.message : String(err)
240
- logger.warn?.(`presets: routing.yml max_steps unavailable (${reason})`)
241
- return {}
242
- }
243
- }
244
-
245
216
  /**
246
217
  * Load the O5 permission.task glob rules from routing.yml
247
218
  * (`permission.task:`). Fail-open: missing config yields null (caller keeps
@@ -165,7 +165,7 @@ async function detectVersion(api) {
165
165
  if (ver) return ver;
166
166
  }
167
167
  } catch {}
168
- return "1.6.0-beta.1";
168
+ return "1.6.0-beta.2";
169
169
  }
170
170
  /**
171
171
  * usage-bar — AI subscription usage gauge for the opencode TUI.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
- "version": "1.6.0-beta.1",
3
+ "version": "1.6.0-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pantheon-tui",
9
- "version": "1.6.0-beta.1",
9
+ "version": "1.6.0-beta.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@opencode-ai/plugin": "1.18.30",
@@ -1529,9 +1529,9 @@
1529
1529
  }
1530
1530
  },
1531
1531
  "node_modules/brace-expansion": {
1532
- "version": "2.1.4",
1533
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
1534
- "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
1532
+ "version": "2.1.7",
1533
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz",
1534
+ "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==",
1535
1535
  "license": "MIT",
1536
1536
  "dependencies": {
1537
1537
  "balanced-match": "^1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
3
  "type": "module",
4
- "version": "1.6.0-beta.1",
4
+ "version": "1.6.0-beta.2",
5
5
  "description": "Pantheon TUI plugin — sidebar (version, preset, sessions, real-time delegations) + AI subscription usage gauges incl. OpenCode Go/Zen dollar limits (vendored, MIT)",
6
6
  "license": "MIT",
7
7
  "engines": {
package/src/routing.yml CHANGED
@@ -99,7 +99,6 @@ presets:
99
99
  agents:
100
100
  zeus:
101
101
  role: Central orchestrator
102
- max_steps: 40 # R4: step budget — at max_steps the agent is forced to summarize-and-stop. Tracker is permanent-per-process (no reset wiring).
103
102
  reasoning_effort: medium
104
103
  user_invocable: true
105
104
  can_delegate: true
@@ -137,7 +136,6 @@ agents:
137
136
  - incremental-implementation
138
137
  athena:
139
138
  role: Strategic planner
140
- max_steps: 15 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
141
139
  reasoning_effort: high
142
140
  user_invocable: true
143
141
  can_delegate: true
@@ -157,7 +155,6 @@ agents:
157
155
  - memory-bank
158
156
  apollo:
159
157
  role: Investigation scout
160
- max_steps: 25 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
161
158
  reasoning_effort: low
162
159
  user_invocable: false
163
160
  can_delegate: false
@@ -175,7 +172,6 @@ agents:
175
172
  - auto-continue
176
173
  hermes:
177
174
  role: Backend specialist
178
- max_steps: 60 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
179
175
  reasoning_effort: medium
180
176
  user_invocable: true
181
177
  can_delegate: true
@@ -198,7 +194,6 @@ agents:
198
194
  - incremental-implementation
199
195
  aphrodite:
200
196
  role: Frontend specialist
201
- max_steps: 60 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
202
197
  reasoning_effort: medium
203
198
  user_invocable: true
204
199
  can_delegate: false
@@ -219,7 +214,6 @@ agents:
219
214
  - file-prompts
220
215
  demeter:
221
216
  role: Database specialist
222
- max_steps: 50 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
223
217
  reasoning_effort: medium
224
218
  user_invocable: true
225
219
  can_delegate: false
@@ -239,7 +233,6 @@ agents:
239
233
  - incremental-implementation
240
234
  themis:
241
235
  role: Quality gate
242
- max_steps: 25 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
243
236
  reasoning_effort: high
244
237
  user_invocable: true
245
238
  can_delegate: false
@@ -261,7 +254,6 @@ agents:
261
254
  - context-compression
262
255
  prometheus:
263
256
  role: Infrastructure specialist
264
- max_steps: 30 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
265
257
  reasoning_effort: medium
266
258
  user_invocable: true
267
259
  can_delegate: false
@@ -280,7 +272,6 @@ agents:
280
272
  - incremental-implementation
281
273
  hephaestus:
282
274
  role: AI pipelines
283
- max_steps: 50 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
284
275
  reasoning_effort: medium
285
276
  user_invocable: true
286
277
  can_delegate: false
@@ -299,7 +290,6 @@ agents:
299
290
  - auto-continue
300
291
  nyx:
301
292
  role: Observability
302
- max_steps: 25 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
303
293
  reasoning_effort: low
304
294
  user_invocable: true
305
295
  can_delegate: false
@@ -318,7 +308,6 @@ agents:
318
308
  - auto-continue
319
309
  gaia:
320
310
  role: Remote sensing domain specialist
321
- max_steps: 25 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
322
311
  reasoning_effort: low
323
312
  user_invocable: false
324
313
  can_delegate: false
@@ -333,7 +322,6 @@ agents:
333
322
  - auto-continue
334
323
  iris:
335
324
  role: GitHub operations
336
- max_steps: 20 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
337
325
  reasoning_effort: low
338
326
  user_invocable: true
339
327
  can_delegate: false
@@ -351,7 +339,6 @@ agents:
351
339
  - artifact-management
352
340
  mnemosyne:
353
341
  role: Memory bank
354
- max_steps: 20 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
355
342
  reasoning_effort: low
356
343
  user_invocable: true
357
344
  can_delegate: false
@@ -375,7 +362,6 @@ agents:
375
362
  - session-goal
376
363
  talos:
377
364
  role: Hotfix agent
378
- max_steps: 15 # R4: step budget — at max_steps the agent is forced to summarize-and-stop
379
365
  reasoning_effort: low
380
366
  user_invocable: true
381
367
  can_delegate: false
@@ -529,7 +515,6 @@ background_job_board:
529
515
  background_delegation:
530
516
  timeout_ms: 900000 # 15 min
531
517
  poll_ms: 250
532
- max_compaction_items: 10
533
518
  prune_ttl_ms: 86400000 # 24h
534
519
  read_only_agents: [apollo, gaia]
535
520
  session_max: 3
@@ -43,7 +43,7 @@ Disciplined automatic continuation through multi-step tasks. Eliminates unnecess
43
43
  - A todo requires a decision not covered by the plan
44
44
  - An unexpected error changes the approach
45
45
  - A dependency is missing or broken
46
- - A task would exceed remaining `steps` budget
46
+ - Remaining context is too thin to carry the task through — write an explicit handoff instead
47
47
 
48
48
  ---
49
49
 
@@ -70,7 +70,7 @@ Disciplined automatic continuation through multi-step tasks. Eliminates unnecess
70
70
  - [ ] Previous step completed successfully (tests pass, no errors)
71
71
  - [ ] Next step is within approved plan scope
72
72
  - [ ] No new blocking issues emerged
73
- - [ ] `steps` counter has sufficient budget remaining
73
+ - [ ] Remaining context can carry the next step, or an explicit handoff will be written
74
74
 
75
75
  ---
76
76