instar 1.3.64 → 1.3.66
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/dist/commands/reflect.d.ts.map +1 -1
- package/dist/commands/reflect.js +2 -1
- package/dist/commands/reflect.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +61 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/core/CircuitBreakingIntelligenceProvider.d.ts +40 -0
- package/dist/core/CircuitBreakingIntelligenceProvider.d.ts.map +1 -0
- package/dist/core/CircuitBreakingIntelligenceProvider.js +72 -0
- package/dist/core/CircuitBreakingIntelligenceProvider.js.map +1 -0
- package/dist/core/ClaudeCliIntelligenceProvider.d.ts.map +1 -1
- package/dist/core/ClaudeCliIntelligenceProvider.js +5 -2
- package/dist/core/ClaudeCliIntelligenceProvider.js.map +1 -1
- package/dist/core/CodexCliIntelligenceProvider.d.ts.map +1 -1
- package/dist/core/CodexCliIntelligenceProvider.js +3 -1
- package/dist/core/CodexCliIntelligenceProvider.js.map +1 -1
- package/dist/core/LlmCircuitBreaker.d.ts +137 -0
- package/dist/core/LlmCircuitBreaker.d.ts.map +1 -0
- package/dist/core/LlmCircuitBreaker.js +236 -0
- package/dist/core/LlmCircuitBreaker.js.map +1 -0
- package/dist/core/intelligenceProviderFactory.d.ts.map +1 -1
- package/dist/core/intelligenceProviderFactory.js +8 -3
- package/dist/core/intelligenceProviderFactory.js.map +1 -1
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/CollaborationRedriveEngine.d.ts +95 -0
- package/dist/monitoring/CollaborationRedriveEngine.d.ts.map +1 -0
- package/dist/monitoring/CollaborationRedriveEngine.js +342 -0
- package/dist/monitoring/CollaborationRedriveEngine.js.map +1 -0
- package/dist/monitoring/CommitmentTracker.d.ts +13 -0
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +10 -0
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.65.md +67 -0
- package/upgrades/1.3.66.md +28 -0
- package/upgrades/side-effects/collaboration-redrive-engine.md +36 -0
- package/upgrades/side-effects/llm-ratelimit-circuit-breaker.md +75 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Fixed a runaway credit-burn bug: LLM call loops no longer keep hammering the
|
|
8
|
+
provider after you hit a usage or spend limit.**
|
|
9
|
+
|
|
10
|
+
Instar runs small background LLM calls to watch your sessions (for example, the
|
|
11
|
+
per-tick check that detects when a session is blocked on a prompt). Until now,
|
|
12
|
+
if one of those calls came back with a usage/rate/spend-limit error, the loop
|
|
13
|
+
swallowed the error and tried again on the very next tick — with no backoff. If
|
|
14
|
+
your account had auto-reload turned on, each retry refueled and re-burned. A
|
|
15
|
+
real agent in the wild burned $452 of $455 in usage credits this way over a
|
|
16
|
+
couple of days before anyone noticed.
|
|
17
|
+
|
|
18
|
+
There is now an **account-global circuit breaker** in front of every internal
|
|
19
|
+
LLM call. The moment the provider reports a usage/rate/spend limit, the breaker
|
|
20
|
+
opens and *all* background LLM-backed work pauses — without spawning another
|
|
21
|
+
`claude` subprocess, so it costs nothing while paused. After a cool-down window
|
|
22
|
+
(15 minutes by default) it sends exactly one quiet probe; if the limit has
|
|
23
|
+
lifted it closes and resumes automatically, and if not it waits another window.
|
|
24
|
+
It is wired at the single provider-construction chokepoint, so every feature —
|
|
25
|
+
current and future — is covered with no per-feature work.
|
|
26
|
+
|
|
27
|
+
This is reactive (it listens to the provider's own "you're over limit" signal)
|
|
28
|
+
and complements the existing volume-based burn-detection, which reacts to
|
|
29
|
+
statistical token-share over a longer window.
|
|
30
|
+
|
|
31
|
+
## What to Tell Your User
|
|
32
|
+
|
|
33
|
+
Your agent can no longer burn through your credits by repeatedly calling the
|
|
34
|
+
model after you've hit a usage or spend limit. The instant the provider says
|
|
35
|
+
you're over your limit, background model work pauses on its own, costs nothing
|
|
36
|
+
while paused, and quietly resumes once the limit lifts. This is on by default —
|
|
37
|
+
no setup needed. If you ever want to tune the cool-down window or turn it off,
|
|
38
|
+
there's an optional circuit-breaker setting in your config, but the safe
|
|
39
|
+
defaults are designed to just work.
|
|
40
|
+
|
|
41
|
+
## Summary of New Capabilities
|
|
42
|
+
|
|
43
|
+
| Capability | How to Use |
|
|
44
|
+
|-----------|-----------|
|
|
45
|
+
| Account-global LLM rate-limit circuit breaker | Automatic. When the provider returns a usage/rate/spend-limit error, all background LLM-backed work pauses (no subprocess spawned) and self-heals via a single probe after the cool-down. |
|
|
46
|
+
| Tunable cool-down / kill switch | Optional `intelligence.circuitBreaker` in `.instar/config.json`: `enabled` (default true) and `openMs` (default 900000 = 15 min). Absent config uses the safe defaults, so existing agents are protected with no changes. |
|
|
47
|
+
|
|
48
|
+
## Evidence
|
|
49
|
+
|
|
50
|
+
**Reproduction (the incident pattern), reproduced as an automated test:** a fake
|
|
51
|
+
`claude` binary that records each spawn to an on-disk counter and exits with
|
|
52
|
+
"Claude AI usage limit reached" on stderr.
|
|
53
|
+
`tests/integration/llm-circuit-breaker-chokepoint.test.ts` builds two providers
|
|
54
|
+
through the real `buildIntelligenceProvider` factory and drives them:
|
|
55
|
+
|
|
56
|
+
- Before fix (old behaviour): every `evaluate()` call spawns the binary →
|
|
57
|
+
unbounded spawns while limited (this is exactly the $452 burn).
|
|
58
|
+
- After fix (observed in the test): the first call spawns once and trips the
|
|
59
|
+
breaker; the next 5 calls — and a call through a *second, independently built*
|
|
60
|
+
provider — all reject with `LlmCircuitOpenError` and the spawn counter stays
|
|
61
|
+
at **1**. Zero additional subprocesses, proving the burn is stopped and the
|
|
62
|
+
breaker is account-global.
|
|
63
|
+
|
|
64
|
+
39 new circuit-breaker tests pass (state machine both-sides-of-boundary, the
|
|
65
|
+
rate-limit classifier on true-positive and unrelated-error strings, and the
|
|
66
|
+
chokepoint wiring). `tsc --noEmit` clean. The server log emits
|
|
67
|
+
`[llm-circuit] OPEN: …` / `[llm-circuit] closing: …` lines on each transition.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Added `CollaborationRedriveEngine` — a small, off-by-default monitor that fills the one verified gap in cross-agent collaboration continuity: nothing today proactively re-engages a counterpart when **they** go silent on an unfinished objective. Spec `docs/specs/collaboration-redrive-on-counterpart-silence.md` (approved by Justin 2026-05-28 after a 2-round adversarial convergence). It builds on the already-shipped Threadline Conversation Keystone (`WarrantsReplyGate` loop-safety + `ConversationStore` continuity + `CompletionEvaluator` done-judgment) — it is **not** a rebuild of any of those.
|
|
8
|
+
|
|
9
|
+
The engine sweeps active `threadline-reply` commitments on its own 5-minute cadence (NOT the PromiseBeacon tick, which only schedules beacon-enabled commitments) and, for each unfinished objective whose counterpart has been silent past a threshold, sends one bounded peer nudge. The termination guarantee is a **durable, monotonic, reply-INDEPENDENT** per-commitment counter (`redriveCount` on the Commitment record) — a counterpart reply updates `lastReplyAt` but never touches the counter, closing the mutual-re-drive hole the round-1 adversarial review caught. After the per-commitment cap (default 2), the engine raises ONE Attention-queue item and goes terminal-quiet on that commitment. Independent per-peer 24h cap, engine-wide daily fuse, per-tick fuse, finite-timestamp validation, and skip-don't-increment on name→fingerprint resolution miss provide defence-in-depth.
|
|
10
|
+
|
|
11
|
+
## What to Tell Your User
|
|
12
|
+
|
|
13
|
+
- I can now nudge another agent once or twice if they go quiet on something we're working on together, then I shut up and surface it to you — never a loop.
|
|
14
|
+
- It ships **OFF**. To turn it on for me, flip the collaboration-redrive enabled setting in my config to true. The other tunables (silence threshold, max nudges, per-peer cap, daily fuse) all have safe defaults.
|
|
15
|
+
- It only triggers on commitments I already have for "I'm waiting on agent X to do thing Y", so it has no effect outside that surface.
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | How to Use |
|
|
20
|
+
|-----------|-----------|
|
|
21
|
+
| Proactive peer re-drive on counterpart silence | Set `monitoring.collaborationRedrive.enabled: true` (defaults: 45-min silence threshold, max 2 nudges per objective, escalate to Attention queue then stop) |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
- Tier-1 unit tests cover both sides of every eligibility boundary (terminal status, NaN/future timestamp, silence threshold, cap, spacing window, missing peer), the load-bearing reply-independent cap (the round-1 adversarial fix), restart-survival across a fresh `CommitmentTracker` load, name-unresolved skip-don't-increment, and disabled-mode no-op.
|
|
26
|
+
- New fields on `Commitment`: `redriveCount?: number`, `lastRedriveAt?: string`, `lastRedriveText?: string` — additive and optional; `loadStore()` backfills `redriveCount: 0` on existing rows alongside the existing `correctionCount`/`escalated` backfill.
|
|
27
|
+
- Side-effects review: `upgrades/side-effects/collaboration-redrive-engine.md`.
|
|
28
|
+
- Spec convergence: `docs/specs/reports/collaboration-redrive-convergence.md` (2 review rounds; round 1 caught a real mutual-re-drive hole reopening the ack-loop blast radius and was fixed by switching to a reply-independent durable cap; round 2 caught a fingerprint-resolution wiring bug and was fixed).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Side-Effects Review — CollaborationRedriveEngine
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/collaboration-redrive-on-counterpart-silence.md` (approved 2026-05-28)
|
|
4
|
+
**Change:** add `src/monitoring/CollaborationRedriveEngine.ts`; add `redriveCount`/`lastRedriveAt`/`lastRedriveText` optional fields to `Commitment`; backfill in `loadStore()`; default new threadline-reply commitments with `redriveCount: 0`; wire construction into `src/commands/server.ts` after `completionEvaluator` (ships OFF).
|
|
5
|
+
|
|
6
|
+
## 1. Over-block / under-block
|
|
7
|
+
**Over-drive:** nudging a counterpart who is legitimately working. Mitigated by the `CompletionEvaluator` "objective-met" gate (auto-closes the commitment instead of nudging), the silence threshold, the per-commitment cap, the per-peer/daily/per-tick fuses, and (decoratively) the novelty tiebreaker. The cap biases toward UNDER-drive (stop + escalate), which is the safe direction.
|
|
8
|
+
**Under-drive:** never nudging → that is today's gap and the thing this fixes. The ship-OFF default is the strongest under-drive bias; we dogfood-on-Echo before any rollout.
|
|
9
|
+
|
|
10
|
+
## 2. Level-of-abstraction fit
|
|
11
|
+
Composes existing primitives — no new transport, no new persistence store. Reads/writes existing `Commitment` records via `mutate()`; sends via the same `threadlineRelayClient.sendPlaintext` the inbound auto-ack path uses; surfaces via the existing `CollaborationSurfacer.notify`; escalates via `telegram.createAttentionItem` (injected `raiseAttention`). The engine is a small monitor sibling to `PromiseBeacon` / `CommitmentTracker`.
|
|
12
|
+
|
|
13
|
+
## 3. Signal vs Authority
|
|
14
|
+
Stall + not-met are SIGNALS. The bounded nudge is a limited action with a hard cap. The OPERATOR holds terminal authority — when the cap is hit, the engine raises an Attention-queue item ("collaboration with <peer> stalled — your call") and goes silent. The engine cannot decide to keep going past the cap.
|
|
15
|
+
|
|
16
|
+
## 4. Interactions
|
|
17
|
+
- `CommitmentTracker`: adds optional fields; uses `getActive()` + `mutate()` + `markReplyArrived()` (already only-touches-`lastReplyAt`).
|
|
18
|
+
- `PromiseBeacon`: independent. The engine runs its OWN sweep so most threadline-reply commitments (which are NOT beacon-enabled) are still covered.
|
|
19
|
+
- `WarrantsReplyGate`: the peer's gate suppresses our nudge if it's pure-ack; the engine sends concrete questions/next-steps so it warrants reply (convergence-driving by construction).
|
|
20
|
+
- `CollaborationSurfacer`: visibility-only — silent hub posts; does not buzz the operator.
|
|
21
|
+
- Attention queue (`telegram.createAttentionItem`): used only at cap-hit + after N consecutive name-resolution misses.
|
|
22
|
+
|
|
23
|
+
## 5. Rollback
|
|
24
|
+
Ships OFF (`monitoring.collaborationRedrive.enabled: false`); flipping back to false fully disables. The new fields are additive/optional — no destructive data transform; the only "migration" is an idempotent backfill defaulting `redriveCount: 0` on existing rows (safe to remove — would just read back as `undefined`, which the engine treats as 0).
|
|
25
|
+
|
|
26
|
+
## 6. Data integrity
|
|
27
|
+
The only mutation to existing Commitment lifecycle semantics is the auto-close on objective-met (transitions `pending → delivered`), which is a documented terminal transition this engine is now authorised to make. All other writes are to the new additive fields. The cap and spacing live durably on the commitment record (via `mutate()`), not in PromiseBeacon hot-state, so they survive restart.
|
|
28
|
+
|
|
29
|
+
## 7. Failure modes
|
|
30
|
+
- **Runaway nudges (incl. mutual re-drive):** bounded by the durable reply-independent cap + per-peer 24h cap + engine-wide daily fuse + per-tick fuse. The cap is the ONLY termination guarantee; novelty is decorative. Tested.
|
|
31
|
+
- **Restart amnesia:** cap/spacing persisted to `commitments.json` via `mutate()`. Restart-survival test required and present.
|
|
32
|
+
- **Clock jump / sleep-wake burst:** per-tick fuse + `Number.isFinite` validation + injectable `now()`. NaN/future timestamps disqualify, fail-safe.
|
|
33
|
+
- **Peer ack-storm in response:** the peer's own `WarrantsReplyGate` suppresses inbound; our durable cap stops our side regardless of replies.
|
|
34
|
+
- **Wrong-peer send:** name→fingerprint resolved via `known-agents.json`; unresolved or ambiguous names skip + don't increment + escalate after N strikes. A send is never attempted against a guessed address.
|
|
35
|
+
- **Re-drive while operator is mid-conversation:** visibility surfaces silently to the Threadline hub; never the active parent topic. Cap-hit escalates to the Attention queue, which the operator opens on their own schedule.
|
|
36
|
+
- **Evaluator unreachable:** errs to "not met" (keep waiting); an evaluator exception never produces an uncounted nudge — but the engine does NOT nudge on evaluator-throw this tick (the evaluator failure short-circuits the eligible path).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Side-effects review — account-global LLM rate-limit circuit breaker
|
|
2
|
+
|
|
3
|
+
**Scope**: Stop the runaway credit-burn failure mode where an LLM-call loop
|
|
4
|
+
keeps invoking the provider after the account is over its usage/spend limit.
|
|
5
|
+
Motivating incident (2026-05-28): a wild agent burned $452/$455 because
|
|
6
|
+
PromptGate's per-tick Haiku detection loop kept spawning `claude -p` against
|
|
7
|
+
the weekly-limit screen with no backoff on the rate-limit error; the account's
|
|
8
|
+
auto-reload refueled the burn each cycle.
|
|
9
|
+
|
|
10
|
+
Distinct from — and complementary to — the existing volume-based
|
|
11
|
+
token-burn-detection system (BurnDetector + LlmRateGate, ~30-min statistical
|
|
12
|
+
reaction). This breaker reacts in milliseconds to the provider's OWN rate-limit
|
|
13
|
+
signal: once the provider says "over limit," every further call is wasted
|
|
14
|
+
(rejected) or harmful (auto-reload), so we stop immediately.
|
|
15
|
+
|
|
16
|
+
**Files touched**:
|
|
17
|
+
- `src/core/LlmCircuitBreaker.ts` — NEW. Account-global breaker
|
|
18
|
+
(closed/open/half-open, single-probe invariant, configurable open window,
|
|
19
|
+
default 15 min) + the pure exported `isRateLimitError()` classifier + typed
|
|
20
|
+
`RateLimitError` / `LlmCircuitOpenError` + process singleton + `configure()`.
|
|
21
|
+
- `src/core/CircuitBreakingIntelligenceProvider.ts` — NEW. Decorator that gates
|
|
22
|
+
every `evaluate()` on the breaker BEFORE delegating; trips on rate-limit,
|
|
23
|
+
closes on any other outcome; `wrapIntelligenceWithCircuitBreaker()` helper
|
|
24
|
+
(null-safe, idempotent).
|
|
25
|
+
- `src/core/intelligenceProviderFactory.ts` — wrap every provider the factory
|
|
26
|
+
returns (the single chokepoint).
|
|
27
|
+
- `src/commands/server.ts` — `configureLlmCircuitBreaker()` from config at
|
|
28
|
+
startup; wrap the three direct `new ClaudeCliIntelligenceProvider` fallback
|
|
29
|
+
sites.
|
|
30
|
+
- `src/commands/reflect.ts` — wrap its direct fallback site.
|
|
31
|
+
- `src/core/ClaudeCliIntelligenceProvider.ts` / `CodexCliIntelligenceProvider.ts`
|
|
32
|
+
— widen the captured stderr slice 200 → 600 so the rate-limit phrase isn't
|
|
33
|
+
truncated before classification.
|
|
34
|
+
- `src/core/types.ts` — optional `intelligence.circuitBreaker` config block.
|
|
35
|
+
- Tests: `tests/unit/LlmCircuitBreaker.test.ts` (31),
|
|
36
|
+
`tests/unit/CircuitBreakingIntelligenceProvider.test.ts` (6),
|
|
37
|
+
`tests/integration/llm-circuit-breaker-chokepoint.test.ts` (2, real subprocess
|
|
38
|
+
spawn-counter proof of account-global short-circuit).
|
|
39
|
+
|
|
40
|
+
**Under-block** (does it fail to stop something it should?): The breaker only
|
|
41
|
+
trips on errors the classifier recognizes as usage/rate/spend limits. If a
|
|
42
|
+
provider emits limit language the classifier doesn't match, the loop is not
|
|
43
|
+
stopped by THIS layer — but the volume-based BurnDetector remains the backstop.
|
|
44
|
+
The classifier is deliberately broad (429/402, "rate limit", "usage limit",
|
|
45
|
+
"quota", "limit reached/resets", "credit balance too low", "payment required",
|
|
46
|
+
"exceeded …"), and the stderr-slice widening reduces truncation misses.
|
|
47
|
+
|
|
48
|
+
**Over-block** (does it stop something it shouldn't?): A false positive trips
|
|
49
|
+
the breaker and degrades ALL LLM-backed features (PromptGate, PresenceProxy,
|
|
50
|
+
PromiseBeacon, sentinels, reviewers) to heuristic-only for one open window
|
|
51
|
+
(default 15 min), after which a single probe self-heals. The classifier
|
|
52
|
+
excludes generic error/timeout/ENOENT/parse text to avoid this. Worst case from
|
|
53
|
+
a false trip is bounded, reversible, and self-healing — and is strictly safer
|
|
54
|
+
than the unbounded credit burn it prevents. A non-rate-limit error during a
|
|
55
|
+
probe closes the breaker (does not keep features down on unrelated errors).
|
|
56
|
+
|
|
57
|
+
**Level-of-abstraction fit**: The breaker is wired at the IntelligenceProvider
|
|
58
|
+
construction chokepoint, so every current and future consumer is covered without
|
|
59
|
+
per-consumer code (Structure > Willpower). Consumers that already swallow
|
|
60
|
+
provider errors (PromptGate's silent fallback) swallow `LlmCircuitOpenError`
|
|
61
|
+
too — the desired behaviour: skip the LLM step at zero subprocess cost.
|
|
62
|
+
|
|
63
|
+
**Signal vs authority**: No new blocking authority over jobs/messages/sessions.
|
|
64
|
+
The breaker only gates LLM dispatch in reaction to the provider's own response.
|
|
65
|
+
It does not decide policy; it enforces "the provider said no."
|
|
66
|
+
|
|
67
|
+
**Migration / fleet reach**: Pure `src/` code shipped via the normal release →
|
|
68
|
+
AutoUpdater path. The protection is default-ON and reads config defensively with
|
|
69
|
+
built-in defaults, so it reaches every existing agent on the version bump with
|
|
70
|
+
NO config migration required. No agent-installed file (hooks/settings/CLAUDE.md
|
|
71
|
+
template/skills) changed → no PostUpdateMigrator entry needed.
|
|
72
|
+
|
|
73
|
+
**Rollback**: `intelligence.circuitBreaker.enabled: false` in `.instar/config.json`
|
|
74
|
+
makes the breaker a transparent passthrough. Full revert is a code revert of the
|
|
75
|
+
additive files.
|