pi-goal-list-loop-audit 0.34.138 → 0.35.4
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 +119 -76
- package/docs/DESIGN.md +103 -16
- package/docs/RELEASING.md +7 -4
- package/extensions/drafter-model.ts +97 -0
- package/extensions/faulty-objective-recovery.ts +4 -0
- package/extensions/goal-commands.ts +79 -23
- package/extensions/goal-continuation.ts +117 -25
- package/extensions/goal-heartbeat.ts +73 -11
- package/extensions/goal-loop-auditor-process.ts +235 -20
- package/extensions/goal-loop-auditor.ts +1 -1
- package/extensions/goal-loop-backoff.ts +9 -69
- package/extensions/goal-loop-core.ts +266 -88
- package/extensions/goal-loop-display.ts +143 -86
- package/extensions/goal-loop-forever.ts +37 -4
- package/extensions/goal-loop-subagents.ts +37 -17
- package/extensions/goal-loop.ts +106 -15
- package/extensions/goal-recovery.ts +354 -154
- package/extensions/goal-settings.ts +181 -53
- package/extensions/goal-state.ts +1 -1
- package/extensions/loops/goal-activation.ts +146 -84
- package/extensions/loops/goal-auditor-hooks.ts +160 -113
- package/extensions/loops/goal-list-queue.ts +236 -14
- package/extensions/loops/goal-orchestrator.ts +53 -48
- package/extensions/loops/goal-runtime-globals.ts +8 -4
- package/extensions/loops/goal-session.ts +167 -38
- package/extensions/loops/goal-settings-ui.ts +251 -63
- package/extensions/loops/goal-tools.ts +106 -48
- package/extensions/loops/goal-ui.ts +13 -18
- package/extensions/loops/goal.ts +3 -5
- package/extensions/main-model-recovery.ts +74 -77
- package/extensions/model-picker.ts +48 -3
- package/extensions/model-selector.ts +13 -1
- package/extensions/multi-model-picker.ts +224 -24
- package/extensions/quota-retry.ts +113 -94
- package/extensions/reviewer.ts +0 -5
- package/extensions/settings-menu.ts +203 -135
- package/package.json +8 -7
- package/prompts/goal-loop-continuation.md +15 -9
- package/prompts/goal-loop-draft.md +10 -0
- package/prompts/goal-loop-forever-draft.md +8 -0
- package/schemas/goal.schema.json +64 -9
- package/scripts/goal-auditor-worker.mjs +66 -6
- package/scripts/smoke.sh +115 -11
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ This is a detached process, not a nested session in the main pi process. `comple
|
|
|
10
10
|
|
|
11
11
|
On Windows, npm installs the `pi.cmd` shim rather than a directly executable `pi` binary. The auditor launches it through an explicitly quoted `cmd.exe` boundary; POSIX keeps direct shell-less execution. Protocol snapshots also tolerate transient Windows file-locks without deleting the last valid snapshot first.
|
|
12
12
|
|
|
13
|
-
**Current package version:** `v0.
|
|
13
|
+
**Current package version:** `v0.35.3` — use `/glla version` to see the installed version and the command for comparing it with the registry latest. This checkout may contain unreleased changes; the npm registry is authoritative for published versions.
|
|
14
14
|
|
|
15
15
|
## Why this exists
|
|
16
16
|
|
|
@@ -122,6 +122,26 @@ ONE confirmed batch, not 50 dialogs.
|
|
|
122
122
|
Note: every list item is audited individually, so at hundreds of items the
|
|
123
123
|
audit cost per item is the thing to think about.
|
|
124
124
|
|
|
125
|
+
Long-running judgment is explicit: the drafter preserves the objective and
|
|
126
|
+
verification contract, prefers a durable root-cause fix, allows a safe,
|
|
127
|
+
reversible workaround when it is genuinely useful, and asks only at an actual
|
|
128
|
+
scope/permission/irreversible-action decision boundary. If you want a design
|
|
129
|
+
checkpoint before implementation, mark a goal, list item, or task-plan entry
|
|
130
|
+
with `Agent: Designer` (also accepted: `Role: designer` or `Designer: yes`).
|
|
131
|
+
The managed Designer is read-only; if it is unavailable, the main agent keeps
|
|
132
|
+
the same checkpoint inline.
|
|
133
|
+
|
|
134
|
+
Drafting can use a separate temporary agent configured in `/glla` under the
|
|
135
|
+
**Drafter** tab (`Drafter agent`, `Drafter thinking`, and `Drafter fallback
|
|
136
|
+
agents`).
|
|
137
|
+
The thinking choice is derived from the selected model, applies only while the
|
|
138
|
+
drafting agent is active, and restores the original session thinking level
|
|
139
|
+
after confirmation or interruption. A drafting failure retries the existing
|
|
140
|
+
interview through that agent's fallback chain. Main-agent and auditor-agent
|
|
141
|
+
recovery chains remain separate. Provider recovery is intentionally blind:
|
|
142
|
+
there is no live quota checking or reset inference; the generic retry envelope
|
|
143
|
+
and the optional hourly `:00:30` probe remain the policy.
|
|
144
|
+
|
|
125
145
|
**Drafting is the default for long-running things.** `/goal` and
|
|
126
146
|
`/loop` with no arguments — and any vague `/list` dump — all start a
|
|
127
147
|
grilling turn that ends in a Confirm dialog. For `/loop` specifically, the orchestrator **test-runs the proposed
|
|
@@ -231,13 +251,17 @@ A persistent `glla:` status segment + an above-editor widget show the current
|
|
|
231
251
|
goal/list item/loop at all times: objective, durable state, elapsed time,
|
|
232
252
|
tokens, next task or loop metric, pause reason, and live auditor progress
|
|
233
253
|
during audits. If something is running, you can see it — no command needed.
|
|
254
|
+
Goal cards label their total wall-clock age explicitly; it includes time parked
|
|
255
|
+
for recovery or waiting on a verdict, so it should not be read as active model
|
|
256
|
+
compute. Recovery cards call the timestamp `last host activity` for the same
|
|
257
|
+
reason: a retry/error event is liveness evidence, not proof of useful work.
|
|
234
258
|
|
|
235
259
|
The status bar is the single activity HUD. It uses compact state capsules plus
|
|
236
260
|
an animated pulse waveform so live work is obvious at a glance without turning
|
|
237
261
|
the line into a progress meter. Fresh stream age is the proof of live work:
|
|
238
262
|
|
|
239
263
|
```text
|
|
240
|
-
glla: [▁▂▄▆█▆ LIVE · WORKING] 1m 09s · last stream 11s ago · 3 queued
|
|
264
|
+
glla: [▁▂▄▆█▆ LIVE · WORKING] total 1m 09s · last stream 11s ago · 3 queued
|
|
241
265
|
glla: [QUEUED] 44s · 18 queued
|
|
242
266
|
```
|
|
243
267
|
|
|
@@ -252,63 +276,62 @@ Activity is otherwise intentionally honest:
|
|
|
252
276
|
| `QUEUED` | A continuation is waiting to start; no work is fabricated. |
|
|
253
277
|
| `IDLE` | The durable item remains active, but no recent work is observed. |
|
|
254
278
|
| `auditor …` | A detached, extension-less verifier is queued, running, quiet, or waiting for its verdict. |
|
|
255
|
-
| `
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
an
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
week" therefore does not cause a week of unattended probes.
|
|
279
|
+
| `RECOVERING` | A bounded automatic retry is pending; the status does not guess why the provider failed. |
|
|
280
|
+
|
|
281
|
+
Detached-auditor elapsed time keeps advancing between worker events. A long
|
|
282
|
+
tool call or hidden reasoning interval therefore remains visibly timed; after
|
|
283
|
+
three minutes without worker activity the UI changes to `auditor quiet` and
|
|
284
|
+
names the cancellation/recovery path instead of presenting a frozen live card.
|
|
285
|
+
|
|
286
|
+
## Provider failures: aggressive retry envelope, bounded (v0.35.0)
|
|
287
|
+
|
|
288
|
+
Error text is **not trusted** to pick a retry policy. The runtime does not
|
|
289
|
+
query or infer provider quota state, and it does not use status codes, billing
|
|
290
|
+
words, rate-limit words, or `Retry-After` hints to choose a branch. Those
|
|
291
|
+
values are retained only as bounded diagnostics. Every recoverable main-model
|
|
292
|
+
failure uses the same durable envelope: an eager 5-second retry, then
|
|
293
|
+
`base → 2×base → 4×base → 8×base → 16×base → 5h`, where `base` is the
|
|
294
|
+
`mainModelRetryMinutes` setting (15 minutes by default). `hourlyRetryProbe=on`
|
|
295
|
+
adds a blind `:00:30` retry after each hour starts, so work can be picked up
|
|
296
|
+
quickly after a possible provider-side change. The automatic window is 24h;
|
|
297
|
+
explicit `/goal resume`, `/list resume`, or `/loop resume` starts a fresh
|
|
298
|
+
window. With global `autoResume=on`, pending retries survive a session reload.
|
|
299
|
+
|
|
300
|
+
Only failures identified by positive evidence as futile avoid automatic retry:
|
|
301
|
+
context/output-token limits and user aborts (`non-recoverable`). Auditor
|
|
302
|
+
watchdog timeouts are also kept separate because rerunning a hanging local
|
|
303
|
+
verification command immediately would repeat the same local failure; the
|
|
304
|
+
stored claim remains available for explicit resume.
|
|
305
|
+
|
|
306
|
+
For continuous work, configure up to **10 ordered Main-agent fallback models**
|
|
307
|
+
in `/glla` using independent model references when possible. The editor is in
|
|
308
|
+
the **Main agent** settings tab (one role per tab, with the current agent,
|
|
309
|
+
thinking level, and fallback chain together). It shows the actual try order as
|
|
310
|
+
`current → fallback 1 → fallback 2 …`, shows each configured fallback's rank,
|
|
311
|
+
lets **Space** add/remove a fallback, and **Tab** enters order mode where
|
|
312
|
+
**↑/↓** moves the highlighted fallback (brackets `[` `]` also reorder without
|
|
313
|
+
leaving the list). Every recoverable provider failure uses the same ordered
|
|
314
|
+
chain: glla calls `setModel` for the first eligible fallback, the next
|
|
315
|
+
supervised turn tests it, and later failures advance left-to-right. Forbidden,
|
|
316
|
+
unavailable, and unauthenticated references are skipped; a successful
|
|
317
|
+
supervised turn clears the episode. The chain is global, durable, and its
|
|
318
|
+
attempted cursor survives reload. After the chain is exhausted, bounded
|
|
319
|
+
retries continue on the active model rather than silently abandoning work.
|
|
320
|
+
The Main agent tab shows the `N/10` count and numbered chain. The Drafter and
|
|
321
|
+
Auditor tabs likewise show each selected model together with its requested
|
|
322
|
+
thinking level; fallback rows show the effective/requested thinking level when
|
|
323
|
+
the model registry exposes the capability map. Press `d` in the settings table
|
|
324
|
+
to toggle the long descriptions and give the model/value column the available
|
|
325
|
+
width.
|
|
326
|
+
|
|
327
|
+
Provider payloads are never copied into chat cards or notifications. A bounded
|
|
328
|
+
diagnostic may remain in the ledger and durable state for forensics, while
|
|
329
|
+
user-facing surfaces use the same generic provider-error label for every
|
|
330
|
+
failure family. The detached auditor follows the same rule: every retriable
|
|
331
|
+
infrastructure failure gets one eager 5-second retry, then stored-claim
|
|
332
|
+
retries at `:00:30` after each hour starts. Its existing 5-attempt/24-hour
|
|
333
|
+
safety envelope prevents an unbounded worker storm; explicit resume starts a
|
|
334
|
+
fresh window.
|
|
312
335
|
|
|
313
336
|
For long-running `/list` work, the card adds a compact queue trail with the
|
|
314
337
|
immediate next item and its truthful wait age while `/list` remains the
|
|
@@ -410,31 +433,48 @@ confirmation is intentionally not bypassed.
|
|
|
410
433
|
Open `/glla` to edit these settings in the table (the rows show effective values and provenance):
|
|
411
434
|
|
|
412
435
|
- Auditor model and thinking level
|
|
413
|
-
- Auditor fallback
|
|
436
|
+
- Auditor fallback agent
|
|
414
437
|
- Notify command, token limit, and wedge-alert minutes
|
|
415
438
|
- Auto-resume, auto-accept drafts, decision popup, and carryover policy
|
|
416
|
-
-
|
|
417
|
-
-
|
|
439
|
+
- Main-agent current model/thinking, fallback models, and recovery cadence in the Main agent tab
|
|
440
|
+
- Drafter agent/thinking/fallback agents in the Drafter tab
|
|
441
|
+
- Auditor agent/thinking/fallback agent in the Auditor tab
|
|
442
|
+
- Forbidden model patterns and switch policy
|
|
443
|
+
- Audit cap/report size, aggressive mode (ON by default), retry cadence, and
|
|
444
|
+
stall brakes
|
|
418
445
|
|
|
419
446
|
The argument namespace is reserved for actions such as `/glla status`, `/glla
|
|
420
447
|
resume`, `/glla cancel`, `/glla stats`, `/glla audits`, `/glla tooloverride`,
|
|
421
|
-
|
|
422
|
-
|
|
448
|
+
`/glla fallbacks clear`, and `/glla wipe`. `fallbacks clear` atomically removes
|
|
449
|
+
the global main-agent fallback chain and cancels any pending fallback switch. Cancel stops the
|
|
450
|
+
active objective; wipe clears all live state while preserving history.
|
|
423
451
|
There is no top-level `/glla key=value` setting syntax.
|
|
424
452
|
|
|
425
|
-
Resolution per key: **project > global > defaults** — EXCEPT `autoResume
|
|
426
|
-
|
|
453
|
+
Resolution per key: **project > global > defaults** — EXCEPT `autoResume` and
|
|
454
|
+
agent recovery settings (`mainModelFallbacks`, `mainModelRetryMinutes`,
|
|
455
|
+
`drafterModel`, `drafterThinkingLevel`, `drafterModelFallbacks`,
|
|
456
|
+
`hourlyRetryProbe`),
|
|
457
|
+
which are **global-only**: per-project opt-ins from old versions
|
|
427
458
|
silently overrode the global hold at launch (the junk-runner incident), so
|
|
428
459
|
the launch-restore gate and the reviewer-enqueue gate read only the global
|
|
429
|
-
file now. Main-session
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
460
|
+
file now. Main-session recovery policy is likewise one global chain/cadence
|
|
461
|
+
for the active session. Main-agent fallback models are global and ordered (up to 10): a provider
|
|
462
|
+
failure selects fallback 1, then fallback 2, and so on, one supervised turn at a
|
|
463
|
+
time. The Main agent tab leads with the ordered-chain editor — a multi-select
|
|
464
|
+
picker where Space toggles membership, Tab enters order mode (↑/↓ moves a
|
|
465
|
+
chain row), and clearing the selection removes the global key. Forbidden,
|
|
466
|
+
unavailable, and unauthenticated refs are skipped. When every candidate is
|
|
467
|
+
down, glla stops the current send attempt and uses the configured
|
|
468
|
+
`base → 2×base → 4×base → 8×base → 16×base → 5h` ladder (`base` defaults to
|
|
469
|
+
15m). `hourlyRetryProbe=on` adds a blind :00:30 retry after each hour starts.
|
|
470
|
+
No provider availability or quota check is made before any retry; all
|
|
471
|
+
recoverable failures walk the ordered fallbacks and then continue on the active
|
|
472
|
+
model through the bounded retry policy. Automatic recovery stops at 24h,
|
|
473
|
+
preserves the saved work, and requires an explicit
|
|
474
|
+
`/goal resume`, `/list resume`, or `/loop resume` to start a fresh window. A
|
|
475
|
+
provider becoming available within that horizon therefore resumes saved work
|
|
476
|
+
without manual intervention; no blind 50ms resend loop is introduced. The
|
|
477
|
+
detached auditor uses an explicit cascade: primary
|
|
438
478
|
`auditorModel` → optional fallback pin → the pi session model. If a selected
|
|
439
479
|
model fails after launch, the worker retries it once and then advances through
|
|
440
480
|
that same cascade; every candidate is still audited in a detached,
|
|
@@ -501,7 +541,10 @@ in-session warning + your configured notify push, once per interval while
|
|
|
501
541
|
it persists. Tune Wedge alert minutes in the `/glla` settings table (0 = off).
|
|
502
542
|
|
|
503
543
|
Every other wait is bounded too: continuation retries are milliseconds,
|
|
504
|
-
|
|
544
|
+
nudge accounting counts consecutive unproductive turns (substantive text
|
|
545
|
+
or tool calls reset the counter) and pauses the goal / stops the loop after
|
|
546
|
+
3 — provider-error and user-abort turns are exempt (v0.27.3+). Measure
|
|
547
|
+
commands get a 10m
|
|
505
548
|
hard timeout, and the detached auditor aborts after 10m with no activity while no
|
|
506
549
|
an auditor tool is running. A long-running verification tool is allowed to
|
|
507
550
|
finish, but each tool has an independent five-minute ceiling and the worker
|
package/docs/DESIGN.md
CHANGED
|
@@ -189,21 +189,40 @@ architectural decisions that changed the SHAPE of the system:
|
|
|
189
189
|
|
|
190
190
|
## Addendum v0.34.31 (main-session model recovery)
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
> Historical rollout note: this section records the older reason-aware
|
|
193
|
+
> recovery implementation. The active policy is the v0.34.142 addendum below;
|
|
194
|
+
> quota/rate-limit wording no longer selects fallback or retry timing.
|
|
195
|
+
|
|
196
|
+
- **Global recovery policy**: `mainModelFallbacks`,
|
|
197
|
+
`mainModelFallbackOnRateLimit`, `mainModelRetryMinutes`, and
|
|
198
|
+
`hourlyQuotaProbe` are global-only, so the runtime and settings provenance
|
|
199
|
+
cannot disagree about which recovery policy is active.
|
|
200
|
+
- **Ordered global fallback agents**: `mainModelFallbacks` is an explicit ordered list
|
|
201
|
+
of up to 10 `provider/model` references. A provider/quota error selects the
|
|
202
|
+
first eligible authenticated candidate for account/plan/billing/auth failures,
|
|
203
|
+
calls `setModel`, and lets the next supervised turn test it; later failures
|
|
204
|
+
walk the list left-to-right. Explicit HTTP 429/request-rate failures also
|
|
205
|
+
walk the list when global `mainModelFallbackOnRateLimit` is on (default); off
|
|
206
|
+
keeps them on the current model with bounded retry + hourly probe cadence.
|
|
207
|
+
The detached auditor's model cascade remains a separate subsystem. The
|
|
208
|
+
ordered-chain editor lives in the **Main agent** settings tab: Space toggles
|
|
209
|
+
membership, Tab enters an explicit order mode where ↑/↓ moves a chain row,
|
|
210
|
+
and clearing the list removes the global key. The attempted cursor is durable, so a reload cannot
|
|
211
|
+
restart at an already-failed rung.
|
|
196
212
|
- **No accepted-send inference**: model rotation occurs only after a provider
|
|
197
213
|
failure is observed (or after a 15-minute, five-minute-silent provider-held
|
|
198
214
|
retry storm). A successful `sendMessage()` return is never treated as a
|
|
199
215
|
started turn.
|
|
200
216
|
- **Durable recovery instead of abandonment**: when all candidates fail,
|
|
201
217
|
`.pi-glla/active.jsonl` stores the primary, active candidate, attempted set,
|
|
202
|
-
retry time, and supervisor kind.
|
|
203
|
-
|
|
204
|
-
|
|
218
|
+
retry time, and supervisor kind. Unhinted recovery waits use the configured
|
|
219
|
+
`mainModelRetryMinutes` base and double per attempt up to 5h; factual
|
|
220
|
+
provider hints win when in budget, while `hourlyQuotaProbe` is a separate
|
|
221
|
+
optional :00:30 ticker. A paused goal/held loop remains resumable. A fresh
|
|
222
|
+
startup obeys the existing `autoResume` consent gate.
|
|
205
223
|
- **Successful-turn reset**: a real non-error agent end clears the recovery
|
|
206
|
-
cycle. Manual model selection cancels it;
|
|
224
|
+
cycle. Manual model selection cancels it; host restore selections do not, and
|
|
225
|
+
user aborts do not masquerade as success. Goal/list/loop cancellation clears
|
|
207
226
|
its timer and durable state.
|
|
208
227
|
|
|
209
228
|
## Addendum v0.34.48–v0.34.56 (lifecycle/recovery hardening — the stale-handle era)
|
|
@@ -240,16 +259,23 @@ replacement without delivering a successor `session_start`:
|
|
|
240
259
|
counterpart are represented as explicitly unmatched facts, never falsely paired —
|
|
241
260
|
the report surface stays truthful (the AuditProgress/AuditorProgress dual-interface
|
|
242
261
|
rule: display evidence-gates on `unmatchedStarts + unmatchedEnds > 0`).
|
|
243
|
-
- **Uniform retry envelope, no text-trust** (v0.34.51): error
|
|
244
|
-
pick a retry policy.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
262
|
+
- **Uniform retry envelope, no text-trust** (v0.34.51 historical baseline): error
|
|
263
|
+
text was not trusted to pick a retry policy. The current ordered-fallback policy
|
|
264
|
+
supersedes that baseline: account/plan/billing/auth failures may walk the configured
|
|
265
|
+
chain, while explicit HTTP 429/request-rate failures walk it when
|
|
266
|
+
`mainModelFallbackOnRateLimit` is on (default), or remain on the current model
|
|
267
|
+
when it is off. Both paths use the configured bounded ladder plus optional
|
|
268
|
+
hourly ticker. Positive-
|
|
269
|
+
evidence futile classes (context/output-token limits, user aborts) plus auditor
|
|
270
|
+
watchdog timeouts never auto-retry; provider hints are honored only within the
|
|
271
|
+
5h probe budget.
|
|
250
272
|
|
|
251
273
|
## Addendum v0.34.57 (quota walls engage recovery fast)
|
|
252
274
|
|
|
275
|
+
> Historical rollout note: the knowledge-window and quota-specific branches
|
|
276
|
+
> described here are retained for design history only and are not active in
|
|
277
|
+
> v0.34.142.
|
|
278
|
+
|
|
253
279
|
- **Knowledge-window escalation**: a surfaced long-lived failure (quota /
|
|
254
280
|
billing / auth) records a 30-minute knowledge window. A send-rearm storm
|
|
255
281
|
inside that window escalates into the recovery envelope after 3 minutes of
|
|
@@ -261,7 +287,25 @@ replacement without delivering a successor `session_start`:
|
|
|
261
287
|
the 5s→3m error ladder and the pi-core retry budget.
|
|
262
288
|
- **Armed by configuration**: the envelope is inert without
|
|
263
289
|
`mainModelFallbacks` (rotation) — an empty list means "park and probe the
|
|
264
|
-
same model" instead of switching pools.
|
|
290
|
+
same model" instead of switching pools. When the list is non-empty, the
|
|
291
|
+
runtime tries one eligible fallback at a time in persisted order, skips
|
|
292
|
+
forbidden/unavailable refs, and parks only after the ordered chain is
|
|
293
|
+
exhausted. Explicit HTTP 429/rate-limit errors remain request-rate signals,
|
|
294
|
+
not token-limit labels; the global `mainModelFallbackOnRateLimit` switch
|
|
295
|
+
controls whether they walk that chain or stay current, and the optional
|
|
296
|
+
:00:30 hourly ticker can retry them.
|
|
297
|
+
|
|
298
|
+
## Addendum Unreleased (process ownership and bounded fallback hardening)
|
|
299
|
+
|
|
300
|
+
- **Worker ownership is durable**: detached auditor locks are rewritten with
|
|
301
|
+
the worker PID, allowing a replacement host to reap stale workers for the
|
|
302
|
+
same completion claim. Parent cancellation waits for exit and escalates to
|
|
303
|
+
the detached POSIX process group or Windows `taskkill /t` tree, so nested
|
|
304
|
+
shells/tests cannot outlive their worker.
|
|
305
|
+
- **Test/smoke teardown is explicit**: direct worker fixtures and the tmux smoke
|
|
306
|
+
harness clean their owned process trees and temporary directories on normal,
|
|
307
|
+
failed, or interrupted exits. Browser processes are not launched by this
|
|
308
|
+
package; external Chrome automation remains outside this lifecycle boundary.
|
|
265
309
|
|
|
266
310
|
## Addendum v0.35.x (one-shot parked completion-audit recovery)
|
|
267
311
|
|
|
@@ -281,6 +325,49 @@ replacement without delivering a successor `session_start`:
|
|
|
281
325
|
remains held unless the existing Auto-resume policy or a validated lifecycle
|
|
282
326
|
handoff supplies consent. `/goal resume` still starts a direct fresh audit.
|
|
283
327
|
|
|
328
|
+
## Addendum v0.34.142 (generic provider recovery)
|
|
329
|
+
|
|
330
|
+
- **No quota availability check exists in the live policy**: provider text,
|
|
331
|
+
status codes, billing/rate-limit words, and upstream retry hints do not
|
|
332
|
+
select a recovery branch, fallback gate, or delay. They may remain as
|
|
333
|
+
bounded diagnostics for forensics and redaction only.
|
|
334
|
+
- **One retry envelope covers every recoverable provider failure**: the first
|
|
335
|
+
retry is eager at 5 seconds, later retries use the configured bounded
|
|
336
|
+
ladder, and the optional `hourlyRetryProbe` adds a blind `:00:30` retry
|
|
337
|
+
after every hour starts. Main-model recovery and detached-auditor recovery
|
|
338
|
+
use this same reason-agnostic rule, with existing context/user-abort and
|
|
339
|
+
safety-horizon exceptions.
|
|
340
|
+
- **Legacy state is inert**: old `quota-waiting` phases, quota-named retry
|
|
341
|
+
counters, and provider-hint fields are accepted only long enough to load
|
|
342
|
+
and normalize old files. Canonical persisted state uses `retry-waiting`,
|
|
343
|
+
`retryAttempts`, `retryFirstAt`, and `retryUntil`.
|
|
344
|
+
|
|
345
|
+
## Addendum v0.35.0 (long-running judgment, Designer, and drafting chain)
|
|
346
|
+
|
|
347
|
+
- **Judgment is a prompt-level contract**: drafting and continuation preserve
|
|
348
|
+
the objective and verification contract, prefer durable root-cause fixes,
|
|
349
|
+
allow only safe/reversible/testable in-scope workarounds, and ask one focused
|
|
350
|
+
question only at a genuine scope, priority, permission, or irreversible-action
|
|
351
|
+
boundary. The unattended fallback is explicit and never infers provider or
|
|
352
|
+
quota state.
|
|
353
|
+
- **Designer routing is explicit, persisted, and read-only**: `Agent: Designer`,
|
|
354
|
+
`Role: designer`, and `Designer: yes` are declarations rather than natural
|
|
355
|
+
language classification. The role is carried by goals, queue items, and task
|
|
356
|
+
plans; continuation injects the hand-off; a managed `Designer.md` uses only
|
|
357
|
+
read/search tools; unavailable role/provider falls back to an inline design
|
|
358
|
+
checkpoint.
|
|
359
|
+
- **Drafting owns a temporary agent lease**: the `/goal`, `/list`, and `/loop`
|
|
360
|
+
interviews resolve a separate primary/fallback chain, select a model-specific
|
|
361
|
+
thinking level, retry the existing interview after generic provider errors,
|
|
362
|
+
and restore the original model and thinking level after confirmation or
|
|
363
|
+
interruption. A current-session last resort is bounded and does not enter
|
|
364
|
+
main-goal recovery. A generation fence and serialized restore prevent
|
|
365
|
+
stale-session or overlapping-draft agent changes.
|
|
366
|
+
- **Host replacement remains host-owned**: Pi event contexts do not expose the
|
|
367
|
+
command-only `newSession`/`fork`/`switchSession` operations. glla therefore
|
|
368
|
+
persists the work and truthfully asks for `/new` when no replacement boundary
|
|
369
|
+
arrives; the proposed event-safe host API is recorded in the audit docs.
|
|
370
|
+
|
|
284
371
|
## Addendum v0.4.0 (completion)
|
|
285
372
|
|
|
286
373
|
- **Auditor compaction enabled** (flaw #3 — the last open one). Safety:
|
package/docs/RELEASING.md
CHANGED
|
@@ -18,10 +18,13 @@ the repository.
|
|
|
18
18
|
## Release checklist
|
|
19
19
|
|
|
20
20
|
Accumulated changes since the last release live under an `## Unreleased`
|
|
21
|
-
section at the top of `CHANGELOG.md
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
section at the top of `CHANGELOG.md`; each milestone gets its own
|
|
22
|
+
`## <version> — <one-line title> (<date>)` header plus `###` topic
|
|
23
|
+
subsections (e.g. `## 0.35.3 — live auditor clock and clearer recovery
|
|
24
|
+
timing (2026-08-15)`). The release commit renames the `Unreleased` section
|
|
25
|
+
to the released version. Do not invent version headers for work that was
|
|
26
|
+
never tagged — untagged work stays under `Unreleased` until the release
|
|
27
|
+
commit.
|
|
25
28
|
|
|
26
29
|
```bash
|
|
27
30
|
npm version <major.minor.patch> --no-git-tag-version
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Drafting-only model resolution and fallback selection.
|
|
2
|
+
//
|
|
3
|
+
// Drafting runs in the main pi session, so the selected model is a temporary
|
|
4
|
+
// lease: the caller must restore the original session model when drafting
|
|
5
|
+
// ends. This module only resolves an ordered candidate list; lifecycle and
|
|
6
|
+
// retries remain in goal-list-queue.ts.
|
|
7
|
+
|
|
8
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
import { isForbiddenModel } from "./goal-loop-core.js";
|
|
11
|
+
import { MAX_MAIN_MODEL_FALLBACKS, modelRef, normalizeMainModelFallbackRefs } from "./main-model-recovery.js";
|
|
12
|
+
import { ModelSelector } from "./model-selector.js";
|
|
13
|
+
import type { Settings } from "./goal-settings.js";
|
|
14
|
+
|
|
15
|
+
export const MAX_DRAFTER_FALLBACKS = MAX_MAIN_MODEL_FALLBACKS;
|
|
16
|
+
|
|
17
|
+
export interface DrafterModelCandidate {
|
|
18
|
+
ref: string;
|
|
19
|
+
model: any;
|
|
20
|
+
via: "configured" | "session-last-resort";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface DrafterModelResolution {
|
|
24
|
+
configuredRefs: string[];
|
|
25
|
+
candidates: DrafterModelCandidate[];
|
|
26
|
+
selected?: DrafterModelCandidate;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Resolve a provider/model ref without making a provider request. */
|
|
30
|
+
export function resolveDrafterModelRef(ctx: Pick<ExtensionContext, "modelRegistry">, ref: string): any | undefined {
|
|
31
|
+
const trimmed = ref.trim();
|
|
32
|
+
if (!trimmed) return undefined;
|
|
33
|
+
const slash = trimmed.indexOf("/");
|
|
34
|
+
try {
|
|
35
|
+
if (slash > 0) {
|
|
36
|
+
const model = ctx.modelRegistry.find(trimmed.slice(0, slash), trimmed.slice(slash + 1));
|
|
37
|
+
if (!model || !ctx.modelRegistry.hasConfiguredAuth(model)) return undefined;
|
|
38
|
+
return model;
|
|
39
|
+
}
|
|
40
|
+
return ctx.modelRegistry
|
|
41
|
+
.getAvailable()
|
|
42
|
+
.filter((candidate: any) => candidate.id === trimmed || candidate.name === trimmed)
|
|
43
|
+
.find((candidate: any) => ctx.modelRegistry.hasConfiguredAuth(candidate));
|
|
44
|
+
} catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the dedicated drafter chain. The current session model is always a
|
|
51
|
+
* final in-process fallback, even when every configured candidate is missing
|
|
52
|
+
* auth or forbidden. No error text, quota state, or retry hint is inspected.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveDrafterModel(ctx: ExtensionContext, settings: Pick<Settings, "drafterModel" | "drafterModelFallbacks" | "forbiddenModels">): DrafterModelResolution {
|
|
55
|
+
const primary = typeof settings.drafterModel === "string" ? settings.drafterModel.trim() : "";
|
|
56
|
+
const configuredRefs = normalizeMainModelFallbackRefs([
|
|
57
|
+
...(primary ? [primary] : []),
|
|
58
|
+
...normalizeMainModelFallbackRefs(settings.drafterModelFallbacks),
|
|
59
|
+
]).slice(0, MAX_DRAFTER_FALLBACKS);
|
|
60
|
+
const currentRef = modelRef(ctx.model);
|
|
61
|
+
const forbidden = (ref: string) => isForbiddenModel(ref, settings.forbiddenModels);
|
|
62
|
+
const selector = new ModelSelector({
|
|
63
|
+
getChain: () => configuredRefs,
|
|
64
|
+
resolve: (ref) => resolveDrafterModelRef(ctx, ref),
|
|
65
|
+
isForbidden: forbidden,
|
|
66
|
+
});
|
|
67
|
+
const attempted: string[] = [];
|
|
68
|
+
const candidates: DrafterModelCandidate[] = [];
|
|
69
|
+
|
|
70
|
+
// ModelSelector deliberately skips the current model so the main recovery
|
|
71
|
+
// walker never selects the model that just failed. Drafting is different:
|
|
72
|
+
// its configured primary may intentionally be the current session model,
|
|
73
|
+
// and that primary still needs a lease so its later fallbacks remain
|
|
74
|
+
// available if the first drafting turn fails.
|
|
75
|
+
const configuredPrimary = configuredRefs[0];
|
|
76
|
+
if (
|
|
77
|
+
configuredPrimary &&
|
|
78
|
+
currentRef &&
|
|
79
|
+
configuredPrimary.toLowerCase() === currentRef.toLowerCase() &&
|
|
80
|
+
!forbidden(configuredPrimary) &&
|
|
81
|
+
ctx.model
|
|
82
|
+
) {
|
|
83
|
+
candidates.push({ ref: configuredPrimary, model: ctx.model, via: "configured" });
|
|
84
|
+
attempted.push(configuredPrimary);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (;;) {
|
|
88
|
+
const selected = selector.selectNextValid({ kind: "drafter" }, currentRef, attempted);
|
|
89
|
+
if (!("model" in selected) || typeof selected.ref !== "string") break;
|
|
90
|
+
attempted.push(selected.ref);
|
|
91
|
+
candidates.push({ ref: selected.ref, model: selected.model, via: "configured" });
|
|
92
|
+
}
|
|
93
|
+
if (ctx.model && currentRef && !candidates.some((candidate) => candidate.ref.toLowerCase() === currentRef.toLowerCase())) {
|
|
94
|
+
candidates.push({ ref: currentRef, model: ctx.model, via: "session-last-resort" });
|
|
95
|
+
}
|
|
96
|
+
return { configuredRefs, candidates, selected: candidates[0] };
|
|
97
|
+
}
|
|
@@ -185,6 +185,10 @@ function proposalFrom(
|
|
|
185
185
|
function approvedCompletionContext(goal: Goal): string | null {
|
|
186
186
|
const latest = goal.auditHistory?.at(-1);
|
|
187
187
|
if (!latest?.approved || latest.disapproved || latest.regressionShieldPassed === false) return null;
|
|
188
|
+
// A completion summary approved for an older contract is not saved intent
|
|
189
|
+
// for the current objective. Legacy verdicts without a revision retain the
|
|
190
|
+
// historical compatibility policy; revisioned verdicts must match exactly.
|
|
191
|
+
if (latest.revision !== undefined && goal.revision !== undefined && latest.revision !== goal.revision) return null;
|
|
188
192
|
return usableCandidate(goal.completionSummary);
|
|
189
193
|
}
|
|
190
194
|
|