instar 1.3.117 → 1.3.119
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/server.d.ts.map +1 -1
- package/dist/commands/server.js +9 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/memory/EmbeddingProvider.d.ts +41 -0
- package/dist/memory/EmbeddingProvider.d.ts.map +1 -1
- package/dist/memory/EmbeddingProvider.js +106 -22
- package/dist/memory/EmbeddingProvider.js.map +1 -1
- package/dist/threadline/ThreadResumeMap.d.ts +8 -1
- package/dist/threadline/ThreadResumeMap.d.ts.map +1 -1
- package/dist/threadline/ThreadResumeMap.js +10 -0
- package/dist/threadline/ThreadResumeMap.js.map +1 -1
- package/dist/threadline/ThreadlineRouter.d.ts +9 -0
- package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
- package/dist/threadline/ThreadlineRouter.js +19 -0
- package/dist/threadline/ThreadlineRouter.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.118.md +51 -0
- package/upgrades/1.3.119.md +40 -0
- package/upgrades/side-effects/onnx-idle-unload.md +36 -0
- package/upgrades/side-effects/threadline-session-complete-retirement.md +94 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Instar Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Threadline now retires completed worker threads promptly when their backing
|
|
8
|
+
session finishes.**
|
|
9
|
+
|
|
10
|
+
Previously, a Threadline conversation bound to a worker session could remain in
|
|
11
|
+
the active-thread set until the stale-retirement backstop cleaned it up, even
|
|
12
|
+
after `SessionManager` had already emitted `sessionComplete` for the backing
|
|
13
|
+
session. That made completed conversations look live longer than they were.
|
|
14
|
+
|
|
15
|
+
Fix: the server now forwards `sessionComplete` into `ThreadlineRouter`, the
|
|
16
|
+
resume map can reverse-lookup live conversations by `boundSessionName`, and the
|
|
17
|
+
router demotes completed non-awaiting conversations through the existing
|
|
18
|
+
`onSessionEnd` lifecycle path. Conversations still waiting on a remote reply stay
|
|
19
|
+
untouched, and the reverse lookup is side-effect-free so a completion event for
|
|
20
|
+
one session cannot archive unrelated stale threads.
|
|
21
|
+
|
|
22
|
+
## What to Tell Your User
|
|
23
|
+
|
|
24
|
+
Threadline conversations will now leave the active set as soon as their worker
|
|
25
|
+
session actually completes, instead of waiting for the old stale cleanup window.
|
|
26
|
+
This makes active-thread state match reality sooner while preserving
|
|
27
|
+
conversations that are still waiting for a peer response.
|
|
28
|
+
|
|
29
|
+
## Summary of New Capabilities
|
|
30
|
+
|
|
31
|
+
| Capability | How to Use |
|
|
32
|
+
|-----------|-----------|
|
|
33
|
+
| Prompt Threadline retirement on session completion | Automatic when a bound worker session emits `sessionComplete` |
|
|
34
|
+
| Awaiting-reply preservation | Automatic — conversations waiting for the peer are not demoted by worker completion |
|
|
35
|
+
|
|
36
|
+
## Evidence
|
|
37
|
+
|
|
38
|
+
- **Focused local tests:** `npm test -- tests/unit/threadline/ThreadlineRouter.test.ts tests/unit/threadline/ThreadResumeMap.test.ts` — 77/77 passed after rebasing onto `v1.3.118`.
|
|
39
|
+
- **Full PR matrix before main refresh:** docs, repo invariants, type, all 8 unit shards on node 20/22, build, integration, e2e, worktree trailer verify, and Vercel passed on PR #568 before the branch was refreshed onto `v1.3.118`.
|
|
40
|
+
- **Side-effects review:** `upgrades/side-effects/threadline-session-complete-retirement.md`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Side-effects — EmbeddingProvider idle-unload
|
|
2
|
+
|
|
3
|
+
**Change:** `EmbeddingProvider` disposes its loaded ONNX pipeline after
|
|
4
|
+
`idleUnloadMs` (default 5 min) of no `embed()` call, lazily reloading on the next
|
|
5
|
+
embed. New `inFlight` guard, rolling idle timer, and a public `dispose()`.
|
|
6
|
+
|
|
7
|
+
## Behavioral side-effects
|
|
8
|
+
|
|
9
|
+
- **First embed after a long idle costs ~1-3s more** (a model reload). All
|
|
10
|
+
subsequent embeds during active use are unaffected — the idle timer resets on
|
|
11
|
+
every embed, so a busy agent keeps the model resident. Memory search/index are
|
|
12
|
+
not latency-critical, so the occasional reload is an acceptable trade for
|
|
13
|
+
eliminating the idle thread-pool spin (~3.6%-44% of a core).
|
|
14
|
+
- **`isReady` is now dynamic.** Previously it returned true forever once loaded;
|
|
15
|
+
now it returns false while the model is unloaded between idle periods. No caller
|
|
16
|
+
uses `isReady` to gate embeds (verified) — `embed()` lazily re-initializes
|
|
17
|
+
regardless — so this is observational only.
|
|
18
|
+
- **Embeddings are unchanged.** Verified live: dispose→reload→embed produces a
|
|
19
|
+
byte-identical vector. Unloading/reloading never alters output.
|
|
20
|
+
- **Default-ON.** The production singleton constructs with defaults, so all agents
|
|
21
|
+
get 5-min idle-unload on update. Set `idleUnloadMs: 0` (constructor config) to
|
|
22
|
+
restore the prior keep-resident behavior.
|
|
23
|
+
|
|
24
|
+
## Blast radius
|
|
25
|
+
|
|
26
|
+
- Scoped to `src/memory/EmbeddingProvider.ts`. SemanticMemory / MemoryIndex /
|
|
27
|
+
TopicMemory consume it transparently (they call `embed()`/`embedBatch()`, which
|
|
28
|
+
handle reload internally). No call-site changes.
|
|
29
|
+
- The idle timer is `unref()`'d — it can never keep the process alive.
|
|
30
|
+
- No config schema, hook, or HTTP changes. No new dependencies.
|
|
31
|
+
|
|
32
|
+
## Migration parity
|
|
33
|
+
|
|
34
|
+
None required — internal runtime logic in `src/`. No agent-installed file
|
|
35
|
+
(`.claude/settings.json`, `.instar/config.json`, CLAUDE.md template, hook, skill)
|
|
36
|
+
changes. Existing agents receive it on their normal version update.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Side-Effects Review — Threadline Session Completion Retirement
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `threadline-session-complete-retirement`
|
|
4
|
+
**Date:** `2026-05-30`
|
|
5
|
+
**Author:** `instar-codey`
|
|
6
|
+
**Second-pass reviewer:** `Feynman`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
This change wires `SessionManager`'s `sessionComplete` event into Threadline thread lifecycle handling. `ThreadResumeMap` gains a reverse lookup from bound tmux session name to live thread records, `ThreadlineRouter` gains `onSessionComplete()` to demote completed non-awaiting thread workers through the existing `onSessionEnd()` path, and `src/commands/server.ts` attaches the listener after the router is constructed. The touched decision point is session lifecycle state transition: whether a live Threadline conversation should leave the active set promptly when its worker session completes.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `ThreadlineRouter.onSessionComplete()` — add — decides which bound threads are demoted on a completed worker session.
|
|
15
|
+
- `ThreadResumeMap.getBySessionName()` — add — lookup primitive only; it returns live conversation state and does not decide.
|
|
16
|
+
- `server.ts sessionComplete listener` — add — forwards a completed tmux session name and optional resume UUID into the router.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Over-block
|
|
21
|
+
|
|
22
|
+
No block/allow surface. The change does not reject inbound or outbound messages. The main over-demotion risk is a thread that is still waiting for the peer being moved to `idle`; this is guarded by checking the underlying ConversationStore state and skipping `awaiting-reply`.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. Under-block
|
|
27
|
+
|
|
28
|
+
No block/allow surface. The remaining miss is any completed worker whose thread record lacks `boundSessionName`; there is no safe reverse mapping for that case, so it will still rely on the 24h stale-retirement backstop. A session with no captured `claudeSessionId` is still demoted using the existing stored UUID.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. Level-of-abstraction fit
|
|
33
|
+
|
|
34
|
+
This is at the Threadline router/session lifecycle layer, which already owns `onSessionEnd(threadId, uuid, sessionName)`. The new method does not reimplement demotion; it reverse-maps session name to thread ids and delegates each eligible thread to the existing router primitive. The raw `awaiting-reply` guard stays near `ConversationStore` state because the legacy `ThreadResumeEntry` view maps `awaiting-reply` to `active`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 4. Signal vs authority compliance
|
|
39
|
+
|
|
40
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
41
|
+
|
|
42
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
43
|
+
|
|
44
|
+
- [ ] No — this change produces a signal consumed by an existing smart gate.
|
|
45
|
+
- [x] No — this change has no block/allow surface.
|
|
46
|
+
- [ ] Yes — but the logic is a smart gate with full conversational context (LLM-backed with recent history or equivalent).
|
|
47
|
+
- [ ] Yes, with brittle logic — STOP. Reshape the design. Brittle detectors must not own block authority. Either promote the logic to smart-gate level (with proper context) or demote it to a signal that feeds an existing smart gate.
|
|
48
|
+
|
|
49
|
+
The logic is a lifecycle transition, not a message judgment. It does not block, filter, or score content. The only deterministic guard is whether the stored lifecycle state is `awaiting-reply`, which is a hard state-machine invariant rather than a brittle semantic detector.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 5. Interactions
|
|
54
|
+
|
|
55
|
+
- **Shadowing:** The listener runs after `SessionManager` emits completion and before the existing 24h stale-retirement backstop would archive stale active entries. It does not shadow inbound routing; future peer replies can still reactivate an idle thread through the normal resume path.
|
|
56
|
+
- **Double-fire:** Multiple completion events for the same tmux session are idempotent enough: already-idle entries may be saved as idle again, and `awaiting-reply` entries remain untouched. The log only emits when at least one matching thread is found.
|
|
57
|
+
- **Races:** The reverse lookup reads active conversation records without running stale-retirement as a lookup side effect, then each demotion uses `ThreadResumeMap.save()`, which writes through the ConversationStore mutation path. A concurrent state change to `awaiting-reply` after lookup remains the main race; the window is small and the next inbound/outbound action can restore state.
|
|
58
|
+
- **Feedback loops:** Demoting a thread reduces active-list noise and prevents the stale backstop from being the first lifecycle update. It does not spawn sessions or send messages.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 6. External surfaces
|
|
63
|
+
|
|
64
|
+
Other agents see more accurate Threadline active-thread state sooner after a worker finishes. Persistent state changes in `{stateDir}/threadline/conversations.json` move eligible records from `active`/`open` to `idle` and refresh the stored session UUID when available. There are no Telegram, Slack, GitHub, Cloudflare, or wire-format changes. Timing depends on `SessionManager` completion detection, but that event already drives other lifecycle systems.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 7. Rollback cost
|
|
69
|
+
|
|
70
|
+
Hot-fix release: revert the listener and helper methods, then ship the next patch. No migration is required. Existing records demoted to `idle` remain valid and can still be resumed/reactivated on the next peer reply; no agent state repair is expected.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Conclusion
|
|
75
|
+
|
|
76
|
+
The change is clear to ship after second-pass review. It narrows the stale-active Threadline window from the 24h backstop to the worker's actual completion event while preserving the important `awaiting-reply` state. Focused regressions cover completed-session demotion and awaiting-reply preservation.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Second-pass review (if required)
|
|
81
|
+
|
|
82
|
+
**Reviewer:** `Feynman`
|
|
83
|
+
**Independent read of the artifact:** `concern resolved`
|
|
84
|
+
|
|
85
|
+
Initial concern: `getBySessionName()` called `retireInactive()` before filtering by session, so a `sessionComplete` event could archive unrelated stale live conversations, including stale `awaiting-reply` records. Resolution: remove stale-retirement from the reverse lookup and add regression coverage that an unrelated stale `awaiting-reply` thread remains untouched during completion handling.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Evidence pointers
|
|
90
|
+
|
|
91
|
+
- `npx vitest run tests/unit/threadline/ThreadlineRouter.test.ts` — 36/36 passed.
|
|
92
|
+
- `npx vitest run tests/unit/threadline/ThreadResumeMap.test.ts` — 41/41 passed.
|
|
93
|
+
- `npm test -- tests/unit/threadline/ThreadlineRouter.test.ts tests/unit/threadline/ThreadResumeMap.test.ts` — 77/77 passed after merging `v1.3.118` main into the PR branch and preparing `v1.3.119` release metadata.
|
|
94
|
+
- `npm run build` — passed; local signing key absent, lockfile signing skipped as documented transitional state.
|