instar 1.3.427 → 1.3.428
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/core/SessionRefresh.d.ts +1 -0
- package/dist/core/SessionRefresh.d.ts.map +1 -1
- package/dist/core/SessionRefresh.js +78 -0
- package/dist/core/SessionRefresh.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.428.md +47 -0
- package/upgrades/side-effects/subscription-swap-transcript-continuity.md +55 -0
|
@@ -28,6 +28,7 @@ import type { SessionManager } from './SessionManager.js';
|
|
|
28
28
|
import type { StateManager } from './StateManager.js';
|
|
29
29
|
import type { TelegramAdapter } from '../messaging/TelegramAdapter.js';
|
|
30
30
|
import type { TopicResumeMap } from './TopicResumeMap.js';
|
|
31
|
+
export declare function ensureResumeTranscriptInConfigHome(uuid: string, targetConfigHome: string): boolean;
|
|
31
32
|
export interface SessionRefreshDeps {
|
|
32
33
|
sessionManager: SessionManager;
|
|
33
34
|
state: StateManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionRefresh.d.ts","sourceRoot":"","sources":["../../src/core/SessionRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"SessionRefresh.d.ts","sourceRoot":"","sources":["../../src/core/SessionRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA2B1D,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CA8BlG;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC;;;;;;;;;;;;;;OAcG;IACH,SAAS,EAAE,CACT,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GAAG,SAAS;IAClC;8EAC0E;IAC1E,WAAW,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KACtD,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,6EAA6E;IAC7E,SAAS,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IACpE;;oDAEgD;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;gBAEvC,IAAI,EAAE,kBAAkB;IAOpC;;;;;;;;;;OAUG;IACG,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAkIlE;;;OAGG;IACH,OAAO,CAAC,cAAc;IAWtB,iEAAiE;IACjE,OAAO,CAAC,aAAa;IAOrB;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAKrB"}
|
|
@@ -24,6 +24,69 @@
|
|
|
24
24
|
* non-Telegram-bound sessions (Slack, iMessage, headless) return
|
|
25
25
|
* { ok: false, code: 'not_telegram_bound' } and remain a follow-up.
|
|
26
26
|
*/
|
|
27
|
+
import fs from 'node:fs';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
/**
|
|
30
|
+
* Account-swap conversation continuity. Claude stores conversation transcripts
|
|
31
|
+
* PER CONFIG HOME (`<CLAUDE_CONFIG_DIR>/projects/<projectDir>/<uuid>.jsonl`), so a
|
|
32
|
+
* quota swap that changes CLAUDE_CONFIG_DIR and then runs `claude --resume <uuid>`
|
|
33
|
+
* finds "No conversation found" — the transcript is still in the OLD account's
|
|
34
|
+
* config home. (The resume UUID is account-agnostic, but the transcript STORAGE
|
|
35
|
+
* is config-home-local — the gap a mocked refresh test can't see.) Before the
|
|
36
|
+
* respawn, copy the transcript into the target config home so --resume succeeds.
|
|
37
|
+
*
|
|
38
|
+
* Self-contained: finds the transcript by uuid across the user's `~/.claude*`
|
|
39
|
+
* config homes (default + enrollment-wizard slots) and copies it, preserving the
|
|
40
|
+
* `projects/<projectDir>/` relative path. Idempotent (no-op if already present),
|
|
41
|
+
* best-effort (never throws). Returns true if the transcript is in the target
|
|
42
|
+
* afterward.
|
|
43
|
+
*/
|
|
44
|
+
function transcriptRelPath(projectsDir, uuid) {
|
|
45
|
+
try {
|
|
46
|
+
for (const proj of fs.readdirSync(projectsDir)) {
|
|
47
|
+
const f = path.join(projectsDir, proj, `${uuid}.jsonl`);
|
|
48
|
+
if (fs.existsSync(f))
|
|
49
|
+
return path.join(proj, `${uuid}.jsonl`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch { /* @silent-fallback-ok: missing/unreadable projects dir */ }
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
export function ensureResumeTranscriptInConfigHome(uuid, targetConfigHome) {
|
|
56
|
+
try {
|
|
57
|
+
const home = process.env.HOME || '';
|
|
58
|
+
const target = targetConfigHome.startsWith('~')
|
|
59
|
+
? path.join(home, targetConfigHome.slice(1))
|
|
60
|
+
: targetConfigHome;
|
|
61
|
+
const targetProjects = path.join(target, 'projects');
|
|
62
|
+
if (transcriptRelPath(targetProjects, uuid))
|
|
63
|
+
return true; // already there → no-op
|
|
64
|
+
let homes = [];
|
|
65
|
+
try {
|
|
66
|
+
homes = fs.readdirSync(home)
|
|
67
|
+
.filter((n) => n === '.claude' || n.startsWith('.claude-'))
|
|
68
|
+
.map((n) => path.join(home, n));
|
|
69
|
+
}
|
|
70
|
+
catch { /* @silent-fallback-ok: HOME unreadable */ }
|
|
71
|
+
for (const ch of homes) {
|
|
72
|
+
if (path.resolve(ch) === path.resolve(target))
|
|
73
|
+
continue;
|
|
74
|
+
const rel = transcriptRelPath(path.join(ch, 'projects'), uuid);
|
|
75
|
+
if (rel) {
|
|
76
|
+
const dst = path.join(targetProjects, rel);
|
|
77
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
78
|
+
fs.copyFileSync(path.join(ch, 'projects', rel), dst);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// @silent-fallback-ok: continuity-copy is best-effort; a failure means the
|
|
86
|
+
// swap may start fresh (logged by the caller), not crash the refresh.
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
27
90
|
const DEFAULT_MAX_PER_WINDOW = 5;
|
|
28
91
|
const DEFAULT_WINDOW_MS = 10 * 60 * 1000;
|
|
29
92
|
export class SessionRefresh {
|
|
@@ -144,6 +207,21 @@ export class SessionRefresh {
|
|
|
144
207
|
const accountSwap = (opts.configHome || opts.accountId)
|
|
145
208
|
? { configHome: opts.configHome, accountId: opts.accountId }
|
|
146
209
|
: undefined;
|
|
210
|
+
// Account-swap continuity: claude stores transcripts per config home, so a
|
|
211
|
+
// swap to a new CLAUDE_CONFIG_DIR must carry the conversation transcript
|
|
212
|
+
// across or `--resume` finds nothing. Skip on `fresh` (we intentionally
|
|
213
|
+
// start a new conversation then). Best-effort: a miss just means the
|
|
214
|
+
// resumed session starts fresh — logged, never fatal.
|
|
215
|
+
if (accountSwap?.configHome && !fresh) {
|
|
216
|
+
const resumeUuid = stateSession.claudeSessionId;
|
|
217
|
+
if (resumeUuid) {
|
|
218
|
+
const ok = ensureResumeTranscriptInConfigHome(resumeUuid, accountSwap.configHome);
|
|
219
|
+
console.log(`[SessionRefresh] account-swap continuity: transcript ${ok ? 'ensured in' : 'NOT found for'} ${accountSwap.configHome} (uuid=${resumeUuid}, sessionName=${sessionName})`);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
console.log(`[SessionRefresh] account-swap continuity: no claudeSessionId on session "${sessionName}" — cannot pre-copy transcript; resumed session may start fresh`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
147
225
|
const newSessionName = await this.deps.respawner(sessionName, topicId, followUpPrompt, accountSwap);
|
|
148
226
|
// ── verify + finalize ────────────────────────────────────────────
|
|
149
227
|
console.log(`[SessionRefresh] Refreshed "${sessionName}" → "${newSessionName}" (topic ${topicId})${reason ? ` reason="${reason}"` : ''}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionRefresh.js","sourceRoot":"","sources":["../../src/core/SessionRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"SessionRefresh.js","sourceRoot":"","sources":["../../src/core/SessionRefresh.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-08T06:00:17.512Z",
|
|
5
|
+
"instarVersion": "1.3.428",
|
|
6
6
|
"entryCount": 199,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Fixes a gap in the quota-swap continuity guarantee: Claude stores conversation
|
|
9
|
+
transcripts per config home, so a swap that changed `CLAUDE_CONFIG_DIR` and then
|
|
10
|
+
ran `claude --resume <uuid>` found "no conversation" — the transcript was still in
|
|
11
|
+
the OLD account's config home, so the conversation was lost (resume → fresh start).
|
|
12
|
+
`SessionRefresh.refreshSession` now copies the transcript into the target account's
|
|
13
|
+
config home (via `ensureResumeTranscriptInConfigHome`) before the `--resume`
|
|
14
|
+
respawn. Best-effort + idempotent; only runs on an account swap; skipped on a
|
|
15
|
+
`fresh` respawn; never throws (a miss falls back to today's fresh-start behavior).
|
|
16
|
+
|
|
17
|
+
## What to Tell Your User
|
|
18
|
+
|
|
19
|
+
When I balance a session across your subscription accounts and one account hits
|
|
20
|
+
its limit, the session now moves to another account AND keeps its conversation —
|
|
21
|
+
it picks up exactly where it left off instead of starting blank. Earlier, the
|
|
22
|
+
move worked but the conversation history didn't follow it across, so a swapped
|
|
23
|
+
session would have lost its context. That gap is closed.
|
|
24
|
+
|
|
25
|
+
## Summary of New Capabilities
|
|
26
|
+
|
|
27
|
+
- **Conversation survives an account swap** — the transcript is carried into the new
|
|
28
|
+
account's config home before resume, so a swapped session continues intact instead
|
|
29
|
+
of starting fresh.
|
|
30
|
+
|
|
31
|
+
## Evidence
|
|
32
|
+
|
|
33
|
+
Caught by live testing (topic 20905) — the merged e2e passed only because it mocked
|
|
34
|
+
the refresh and never moved a real conversation between real config homes.
|
|
35
|
+
|
|
36
|
+
Reproduction (real accounts):
|
|
37
|
+
- Started a conversation under the SageMind config home (it recalled a test marker).
|
|
38
|
+
- Tried to resume that session id under the Justin config home (as a swap would):
|
|
39
|
+
**Before:** `claude --resume <uuid>` → "No conversation found with session ID …"
|
|
40
|
+
— the conversation is lost.
|
|
41
|
+
**After:** the shipped `ensureResumeTranscriptInConfigHome` copies the transcript
|
|
42
|
+
into the Justin config home, then `claude --resume <uuid>` → succeeds and correctly
|
|
43
|
+
recalls the marker (verified end-to-end with marker SWAP-FIX-VERIFY-8852).
|
|
44
|
+
|
|
45
|
+
Confirmed transcripts are config-home-local: a conversation run under
|
|
46
|
+
`~/.claude-echo-sagemind` wrote to `~/.claude-echo-sagemind/projects/…`, not the
|
|
47
|
+
default `~/.claude/projects`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Side-Effects Review — account-swap conversation continuity (transcript copy)
|
|
2
|
+
|
|
3
|
+
## Scope of change
|
|
4
|
+
|
|
5
|
+
- `src/core/SessionRefresh.ts` — new exported `ensureResumeTranscriptInConfigHome(uuid,
|
|
6
|
+
targetConfigHome)` + a guarded call in `refreshSession`, just before the respawner,
|
|
7
|
+
when `accountSwap.configHome` is set and not a `fresh` respawn. Copies the conversation
|
|
8
|
+
transcript into the target account's config home so `claude --resume` finds it.
|
|
9
|
+
|
|
10
|
+
## The gap (caught by live testing, not the mocked e2e)
|
|
11
|
+
|
|
12
|
+
Claude stores transcripts PER CONFIG HOME: `<CLAUDE_CONFIG_DIR>/projects/<projectDir>/
|
|
13
|
+
<uuid>.jsonl`. The P1.3 swap changes `CLAUDE_CONFIG_DIR` to the new account then runs
|
|
14
|
+
`claude --resume <uuid>` — which looks in the NEW config home and finds nothing, because
|
|
15
|
+
the transcript is still in the OLD account's config home. So the "continuity guarantee"
|
|
16
|
+
(the headline feature) would silently LOSE the conversation (resume → fresh start). The
|
|
17
|
+
P1.3 comment "the resume UUID is account-agnostic" was right about the UUID but missed
|
|
18
|
+
that transcript STORAGE is config-home-local. The merged e2e passed only because it
|
|
19
|
+
mocked the refresh — it never moved a real conversation between real config homes.
|
|
20
|
+
|
|
21
|
+
## The fix
|
|
22
|
+
|
|
23
|
+
Before the `--resume` respawn under the new config home, find the transcript by uuid
|
|
24
|
+
across the user's `~/.claude*` config homes (default + enrollment-wizard slots) and copy
|
|
25
|
+
it into the target config home, preserving the `projects/<projectDir>/` relative path.
|
|
26
|
+
Idempotent (no-op if already present), best-effort (never throws — a miss is logged and
|
|
27
|
+
the session simply starts fresh, the prior behavior).
|
|
28
|
+
|
|
29
|
+
## Authority / autonomy
|
|
30
|
+
|
|
31
|
+
No new authority. Only runs on an account swap (`accountSwap.configHome` set), which is
|
|
32
|
+
itself opt-in/dark. Touches only the per-account `projects/` transcript files (copy, never
|
|
33
|
+
delete). When no swap is requested — every existing refresh/recovery — this code does not
|
|
34
|
+
run; behaviour is byte-for-byte unchanged.
|
|
35
|
+
|
|
36
|
+
## Framework generality
|
|
37
|
+
|
|
38
|
+
Claude-code-specific by nature (the `<CLAUDE_CONFIG_DIR>/projects/.../<uuid>.jsonl` layout
|
|
39
|
+
is Claude's), matching the standard's Claude-first scope. The account-swap path is only
|
|
40
|
+
taken for claude-code sessions; other frameworks don't set `configHome`.
|
|
41
|
+
|
|
42
|
+
## Failure modes considered
|
|
43
|
+
|
|
44
|
+
- `fresh` respawn → skipped (we intentionally start a new conversation).
|
|
45
|
+
- No `claudeSessionId` on the session → logged, skipped (can't know the uuid); resumed
|
|
46
|
+
session may start fresh — same as today, not a regression.
|
|
47
|
+
- Transcript exists nowhere / unreadable → returns false, logged; respawn proceeds (fresh).
|
|
48
|
+
- Already in the target → no-op (idempotent).
|
|
49
|
+
- Config home outside `~/.claude*` → not found by the search (documented limitation; all
|
|
50
|
+
current config homes — default + wizard slots — live under HOME).
|
|
51
|
+
|
|
52
|
+
## Migration / parity
|
|
53
|
+
|
|
54
|
+
Pure additive logic on files claude already writes — no migration, no config, no stored
|
|
55
|
+
shape change. Ships via dist.
|