@alsdd/t3-import 0.0.0-stage → 0.1.1-alsd.1
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 +24 -0
- package/LICENSE +21 -0
- package/README.md +141 -2
- package/assets/fork-demo.gif +0 -0
- package/assets/fork-demo.png +0 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +399 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/checkpoint.d.ts +25 -0
- package/dist/core/checkpoint.js +82 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +17 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/jsonl.d.ts +7 -0
- package/dist/core/jsonl.js +37 -0
- package/dist/core/jsonl.js.map +1 -0
- package/dist/core/providers.d.ts +6 -0
- package/dist/core/providers.js +17 -0
- package/dist/core/providers.js.map +1 -0
- package/dist/core/types.d.ts +247 -0
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/util.d.ts +14 -0
- package/dist/core/util.js +61 -0
- package/dist/core/util.js.map +1 -0
- package/dist/sources/claude.d.ts +6 -0
- package/dist/sources/claude.js +576 -0
- package/dist/sources/claude.js.map +1 -0
- package/dist/sources/codex.d.ts +11 -0
- package/dist/sources/codex.js +603 -0
- package/dist/sources/codex.js.map +1 -0
- package/dist/sources/codexAppServer.d.ts +24 -0
- package/dist/sources/codexAppServer.js +190 -0
- package/dist/sources/codexAppServer.js.map +1 -0
- package/dist/sources/files.d.ts +1 -0
- package/dist/sources/files.js +24 -0
- package/dist/sources/files.js.map +1 -0
- package/dist/sources/index.d.ts +3 -0
- package/dist/sources/index.js +15 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/sources/opencode.d.ts +6 -0
- package/dist/sources/opencode.js +443 -0
- package/dist/sources/opencode.js.map +1 -0
- package/dist/target/cache.d.ts +14 -0
- package/dist/target/cache.js +155 -0
- package/dist/target/cache.js.map +1 -0
- package/dist/target/config.d.ts +22 -0
- package/dist/target/config.js +90 -0
- package/dist/target/config.js.map +1 -0
- package/dist/target/destination.d.ts +21 -0
- package/dist/target/destination.js +77 -0
- package/dist/target/destination.js.map +1 -0
- package/dist/target/importer.d.ts +86 -0
- package/dist/target/importer.js +597 -0
- package/dist/target/importer.js.map +1 -0
- package/dist/target/launch.d.ts +6 -0
- package/dist/target/launch.js +77 -0
- package/dist/target/launch.js.map +1 -0
- package/dist/target/ledger.d.ts +35 -0
- package/dist/target/ledger.js +174 -0
- package/dist/target/ledger.js.map +1 -0
- package/dist/target/reconciliation.d.ts +60 -0
- package/dist/target/reconciliation.js +259 -0
- package/dist/target/reconciliation.js.map +1 -0
- package/dist/target/replace.d.ts +9 -0
- package/dist/target/replace.js +341 -0
- package/dist/target/replace.js.map +1 -0
- package/dist/target/schema.d.ts +22 -0
- package/dist/target/schema.js +108 -0
- package/dist/target/schema.js.map +1 -0
- package/dist/target/sync.d.ts +34 -0
- package/dist/target/sync.js +879 -0
- package/dist/target/sync.js.map +1 -0
- package/dist/target/syncAll.d.ts +4 -0
- package/dist/target/syncAll.js +98 -0
- package/dist/target/syncAll.js.map +1 -0
- package/dist/target/v2.d.ts +18 -0
- package/dist/target/v2.js +267 -0
- package/dist/target/v2.js.map +1 -0
- package/dist/target/v2Profiles.d.ts +5 -0
- package/dist/target/v2Profiles.js +14 -0
- package/dist/target/v2Profiles.js.map +1 -0
- package/dist/tui.d.ts +49 -0
- package/dist/tui.js +560 -0
- package/dist/tui.js.map +1 -0
- package/package.json +93 -4
- package/vendor/t3-v010/LICENSE +21 -0
- package/vendor/t3-v010/THIRD-PARTY-NOTICES.txt +2274 -0
- package/vendor/t3-v010/reference.json +6 -0
- package/vendor/t3-v010/runtime.mjs +60364 -0
- package/vendor/t3-v010/schema-columns.json +568 -0
- package/vendor/t3-v011/LICENSE +21 -0
- package/vendor/t3-v011/THIRD-PARTY-NOTICES.txt +2274 -0
- package/vendor/t3-v011/reference.json +6 -0
- package/vendor/t3-v011/runtime.mjs +60993 -0
- package/vendor/t3-v011/schema-columns.json +526 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { isObject, stringValue } from "../core/util.js";
|
|
4
|
+
import { sourceDriver, sourceFromDriver, sourceNames } from "../core/providers.js";
|
|
5
|
+
import { compatibilityError, safetyError } from "../core/errors.js";
|
|
6
|
+
import { inspectRuntime, isV2Database, validateTargetDatabase } from "./schema.js";
|
|
7
|
+
function parseObject(value) {
|
|
8
|
+
if (!value)
|
|
9
|
+
return undefined;
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(value);
|
|
12
|
+
return isObject(parsed) ? parsed : undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function v2ThreadRows(db) {
|
|
19
|
+
return db.prepare(`SELECT t.thread_id threadId, t.title, t.project_id projectId,
|
|
20
|
+
p.title projectTitle, p.workspace_root projectRoot, json_extract(t.payload_json,'$.worktreePath') worktreePath,
|
|
21
|
+
t.archived_at archivedAt, t.deleted_at deletedAt, t.payload_json payload
|
|
22
|
+
FROM orchestration_v2_projection_threads t
|
|
23
|
+
LEFT JOIN projection_projects p ON p.project_id=t.project_id
|
|
24
|
+
ORDER BY t.created_at, t.thread_id`).all();
|
|
25
|
+
}
|
|
26
|
+
function v1ThreadRows(db) {
|
|
27
|
+
const projected = db.prepare(`SELECT t.thread_id threadId, t.title, t.project_id projectId,
|
|
28
|
+
p.title projectTitle, p.workspace_root projectRoot, t.worktree_path worktreePath,
|
|
29
|
+
t.archived_at archivedAt, t.deleted_at deletedAt, NULL payload
|
|
30
|
+
FROM projection_threads t
|
|
31
|
+
LEFT JOIN projection_projects p ON p.project_id=t.project_id
|
|
32
|
+
ORDER BY t.created_at, t.thread_id`).all();
|
|
33
|
+
const projects = new Map();
|
|
34
|
+
for (const row of db.prepare("SELECT project_id id,title,workspace_root root FROM projection_projects").all())
|
|
35
|
+
projects.set(row.id, { title: row.title, root: row.root });
|
|
36
|
+
for (const row of db.prepare("SELECT stream_id id,event_type type,payload_json payload FROM orchestration_events WHERE aggregate_kind='project' ORDER BY sequence").all()) {
|
|
37
|
+
const payload = parseObject(row.payload);
|
|
38
|
+
if (!payload)
|
|
39
|
+
continue;
|
|
40
|
+
const title = stringValue(payload.title);
|
|
41
|
+
const root = stringValue(payload.workspaceRoot);
|
|
42
|
+
if (title && root && row.type !== "project.deleted")
|
|
43
|
+
projects.set(row.id, { title, root });
|
|
44
|
+
if (row.type === "project.deleted")
|
|
45
|
+
projects.delete(row.id);
|
|
46
|
+
}
|
|
47
|
+
const threads = new Map();
|
|
48
|
+
for (const row of db.prepare("SELECT stream_id id,event_type type,payload_json payload FROM orchestration_events WHERE aggregate_kind='thread' ORDER BY sequence").all()) {
|
|
49
|
+
const payload = parseObject(row.payload);
|
|
50
|
+
if (!payload)
|
|
51
|
+
continue;
|
|
52
|
+
const current = threads.get(row.id);
|
|
53
|
+
if (row.type === "thread.created") {
|
|
54
|
+
const projectId = stringValue(payload.projectId) ?? null;
|
|
55
|
+
const project = projectId ? projects.get(projectId) : undefined;
|
|
56
|
+
threads.set(row.id, {
|
|
57
|
+
threadId: row.id, title: stringValue(payload.title) ?? row.id, projectId,
|
|
58
|
+
projectTitle: project?.title ?? null, projectRoot: project?.root ?? null,
|
|
59
|
+
worktreePath: stringValue(payload.worktreePath) ?? null,
|
|
60
|
+
archivedAt: stringValue(payload.archivedAt) ?? null, deletedAt: stringValue(payload.deletedAt) ?? null, payload: null,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (current && (row.type === "thread.meta-updated" || row.type === "thread.deleted")) {
|
|
64
|
+
const projectId = stringValue(payload.projectId) ?? current.projectId;
|
|
65
|
+
const project = projectId ? projects.get(projectId) : undefined;
|
|
66
|
+
threads.set(row.id, {
|
|
67
|
+
...current, title: stringValue(payload.title) ?? current.title, projectId,
|
|
68
|
+
projectTitle: project?.title ?? current.projectTitle, projectRoot: project?.root ?? current.projectRoot,
|
|
69
|
+
worktreePath: typeof payload.worktreePath === "string" ? payload.worktreePath : current.worktreePath,
|
|
70
|
+
archivedAt: typeof payload.archivedAt === "string" ? payload.archivedAt : current.archivedAt,
|
|
71
|
+
deletedAt: row.type === "thread.deleted" ? stringValue(payload.deletedAt) ?? "deleted" : current.deletedAt,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const row of projected)
|
|
76
|
+
if (!threads.has(row.threadId))
|
|
77
|
+
threads.set(row.threadId, row);
|
|
78
|
+
return [...threads.values()].sort((a, b) => a.threadId.localeCompare(b.threadId));
|
|
79
|
+
}
|
|
80
|
+
function v2Bindings(db, threadId, thread) {
|
|
81
|
+
const rows = db.prepare("SELECT provider_thread_id providerThreadId, thread_id threadId, driver, provider_instance_id providerInstanceId, payload_json payload FROM orchestration_v2_projection_provider_threads WHERE thread_id=?").all(threadId);
|
|
82
|
+
const appThreadId = stringValue(thread.id) ?? threadId;
|
|
83
|
+
const candidates = rows.flatMap((row) => {
|
|
84
|
+
const payload = parseObject(row.payload);
|
|
85
|
+
if (!payload || (stringValue(payload.appThreadId) ?? row.threadId) !== appThreadId)
|
|
86
|
+
return [];
|
|
87
|
+
const ref = isObject(payload.nativeThreadRef) ? payload.nativeThreadRef : undefined;
|
|
88
|
+
const driver = stringValue(ref?.driver) ?? row.driver ?? stringValue(payload.driver);
|
|
89
|
+
const source = driver ? sourceFromDriver(driver) : undefined;
|
|
90
|
+
if (!driver || !source)
|
|
91
|
+
return [];
|
|
92
|
+
const strength = ref?.strength === "strong" || ref?.strength === "weak" ? ref.strength : "none";
|
|
93
|
+
return [{
|
|
94
|
+
source, driver, nativeId: stringValue(ref?.nativeId) ?? null, strength,
|
|
95
|
+
providerInstanceId: stringValue(payload.providerInstanceId) ?? row.providerInstanceId,
|
|
96
|
+
status: stringValue(payload.status) ?? null,
|
|
97
|
+
providerThreadId: stringValue(payload.id) ?? row.providerThreadId,
|
|
98
|
+
}];
|
|
99
|
+
});
|
|
100
|
+
const activeId = stringValue(thread.activeProviderThreadId);
|
|
101
|
+
const selected = activeId ? candidates.filter((item) => item.providerThreadId === activeId) : candidates;
|
|
102
|
+
return selected;
|
|
103
|
+
}
|
|
104
|
+
function migrationEvidence(db, threadId, thread, bindings, historical) {
|
|
105
|
+
if (!historical.length || thread?.historyOrigin !== "v1_import")
|
|
106
|
+
return null;
|
|
107
|
+
const current = bindings.filter((binding) => binding.nativeId);
|
|
108
|
+
if (!current.length)
|
|
109
|
+
return { status: "legacy-only", handoffIds: [] };
|
|
110
|
+
const rows = db.prepare("SELECT payload_json payload FROM orchestration_v2_projection_context_handoffs WHERE thread_id=?").all(threadId);
|
|
111
|
+
const handoffIds = rows.flatMap((row) => {
|
|
112
|
+
const handoff = parseObject(row.payload);
|
|
113
|
+
if (!handoff || handoff.threadId !== threadId || handoff.strategy !== "manual_context" || handoff.status !== "ready")
|
|
114
|
+
return [];
|
|
115
|
+
const target = current.find((binding) => binding.providerThreadId === handoff.toProviderThreadId);
|
|
116
|
+
const covered = isObject(handoff.coveredRunOrdinals) ? handoff.coveredRunOrdinals : undefined;
|
|
117
|
+
// The cutover's first-run handoff establishes continuity, not equal transcripts.
|
|
118
|
+
if (!target || covered?.from !== 1 || covered?.to !== 1)
|
|
119
|
+
return [];
|
|
120
|
+
return stringValue(handoff.id) ? [String(handoff.id)] : [];
|
|
121
|
+
});
|
|
122
|
+
return { status: handoffIds.length ? "continued" : "unverified", handoffIds };
|
|
123
|
+
}
|
|
124
|
+
function v1Binding(db, threadId) {
|
|
125
|
+
const row = db.prepare(`SELECT adapter_key adapterKey, provider_instance_id providerInstanceId,
|
|
126
|
+
status, resume_cursor_json cursor, runtime_payload_json runtimePayload
|
|
127
|
+
FROM provider_session_runtime WHERE thread_id=?`).get(threadId);
|
|
128
|
+
if (!row)
|
|
129
|
+
return { imported: false, evidence: [] };
|
|
130
|
+
const source = sourceFromDriver(row.adapterKey);
|
|
131
|
+
if (!source)
|
|
132
|
+
return { imported: false, evidence: ["V1 provider runtime uses an unrecognized adapter key."] };
|
|
133
|
+
const cursor = parseObject(row.cursor);
|
|
134
|
+
const runtime = parseObject(row.runtimePayload);
|
|
135
|
+
const nativeId = source === "claude" ? stringValue(cursor?.resume)
|
|
136
|
+
: source === "opencode" ? stringValue(cursor?.sessionId)
|
|
137
|
+
: stringValue(cursor?.threadId);
|
|
138
|
+
const imported = stringValue(runtime?.lastRuntimeEvent)?.startsWith("t3-import.") ?? false;
|
|
139
|
+
return {
|
|
140
|
+
binding: { source, driver: row.adapterKey, nativeId: nativeId ?? null, strength: nativeId ? "strong" : "none", providerInstanceId: row.providerInstanceId, status: row.status },
|
|
141
|
+
imported,
|
|
142
|
+
evidence: ["Legacy provider_session_runtime binding", ...(nativeId ? ["Native ID read from the provider resume cursor"] : ["Resume cursor does not contain a recognized native ID"]), ...(imported ? ["Importer runtime marker identifies a prior import"] : [])],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function readV2ImportMetadata(db, threadId) {
|
|
146
|
+
const rows = db.prepare("SELECT metadata_json metadata FROM orchestration_events WHERE aggregate_kind='thread' AND stream_id=? AND event_type='thread.created'").all(threadId);
|
|
147
|
+
for (const row of rows) {
|
|
148
|
+
const metadata = parseObject(row.metadata);
|
|
149
|
+
const value = isObject(metadata?.t3Import) ? metadata.t3Import : undefined;
|
|
150
|
+
const source = typeof value?.source === "string" && sourceNames.includes(value.source) ? value.source : undefined;
|
|
151
|
+
const sourceSessionId = stringValue(value?.sourceSessionId);
|
|
152
|
+
if (value?.schemaVersion === 1 && value.canonical === true && source && sourceSessionId)
|
|
153
|
+
return { source, sourceSessionId };
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
function originOf(thread, importedMetadata, v1Imported = false) {
|
|
158
|
+
if (importedMetadata || v1Imported)
|
|
159
|
+
return "imported";
|
|
160
|
+
if (thread?.historyOrigin === "v1_import")
|
|
161
|
+
return "unknown";
|
|
162
|
+
if (thread?.historyOrigin === "native" || stringValue(thread?.creationSource))
|
|
163
|
+
return "native";
|
|
164
|
+
return "unknown";
|
|
165
|
+
}
|
|
166
|
+
function buildRows(db, v2, options) {
|
|
167
|
+
const records = v2 ? v2ThreadRows(db) : v1ThreadRows(db);
|
|
168
|
+
const out = records.flatMap((row) => {
|
|
169
|
+
if (row.deletedAt)
|
|
170
|
+
return [];
|
|
171
|
+
const projected = parseObject(row.payload);
|
|
172
|
+
const archived = Boolean(row.archivedAt) || Boolean(projected?.archivedAt);
|
|
173
|
+
const lineage = isObject(projected?.lineage) ? projected.lineage : {};
|
|
174
|
+
const subagent = lineage.relationshipToParent === "subagent";
|
|
175
|
+
const bindings = v2 ? v2Bindings(db, row.threadId, projected ?? {}) : [];
|
|
176
|
+
const legacy = v1Binding(db, row.threadId);
|
|
177
|
+
const historicalBindings = v2 && legacy.binding?.nativeId ? [legacy.binding] : [];
|
|
178
|
+
if (!v2 && legacy.binding)
|
|
179
|
+
bindings.push(legacy.binding);
|
|
180
|
+
const importMetadata = v2 ? readV2ImportMetadata(db, row.threadId) : undefined;
|
|
181
|
+
if (importMetadata && !bindings.some((binding) => binding.source === importMetadata.source && binding.nativeId === importMetadata.sourceSessionId)) {
|
|
182
|
+
bindings.push({ source: importMetadata.source, driver: sourceDriver(importMetadata.source), nativeId: importMetadata.sourceSessionId, strength: "strong", providerInstanceId: stringValue(projected?.providerInstanceId) ?? null, status: "imported" });
|
|
183
|
+
}
|
|
184
|
+
const includeInReport = (!archived || Boolean(options.includeArchived))
|
|
185
|
+
&& (!subagent || Boolean(options.includeSubagents))
|
|
186
|
+
&& (!options.source || [...bindings, ...historicalBindings].some((binding) => binding.source === options.source));
|
|
187
|
+
const strong = bindings.filter((binding) => binding.strength === "strong" && binding.nativeId);
|
|
188
|
+
const localAmbiguity = new Set(strong.map((binding) => `${binding.source}\u0000${binding.nativeId}`)).size > 1;
|
|
189
|
+
const mapping = localAmbiguity ? "ambiguous" : strong.length ? "strong" : bindings.some((binding) => binding.strength === "weak") ? "weak" : historicalBindings.length ? "historical" : "unlinked";
|
|
190
|
+
const provider = strong[0] ?? bindings[0];
|
|
191
|
+
const effectiveCwd = row.worktreePath ?? row.projectRoot;
|
|
192
|
+
const migration = v2 ? migrationEvidence(db, row.threadId, projected, bindings, historicalBindings) : null;
|
|
193
|
+
const importMismatch = Boolean(importMetadata && bindings.some((binding) => binding.strength === "strong" && binding.nativeId && (binding.source !== importMetadata.source || binding.nativeId !== importMetadata.sourceSessionId)));
|
|
194
|
+
const evidence = v2
|
|
195
|
+
? [projected?.historyOrigin === "v1_import" ? "V2 app-thread historyOrigin is v1_import" : "V2 app-thread projection inspected", ...(provider?.providerThreadId ? ["Provider-thread nativeThreadRef inspected"] : ["No native provider-thread reference found"]), ...(historicalBindings.length ? ["Retained V1 resume cursor proves historical provider identity, not the current V2 session"] : []), ...(migration?.status === "continued" ? ["First-run manual_context handoff links legacy app history to the current V2 provider thread"] : []), ...(importMetadata ? ["Canonical thread.created metadata carries the t3Import source identity"] : [])]
|
|
196
|
+
: legacy?.evidence ?? ["V1 projected thread inspected"];
|
|
197
|
+
const ambiguity = [
|
|
198
|
+
...(localAmbiguity ? ["This T3 thread has multiple distinct strong native-provider bindings."] : []),
|
|
199
|
+
...(importMismatch ? ["The canonical t3Import source identity disagrees with a strong provider native reference."] : []),
|
|
200
|
+
];
|
|
201
|
+
return [{
|
|
202
|
+
threadId: row.threadId, title: row.title, projectId: row.projectId, projectTitle: row.projectTitle, projectRoot: row.projectRoot,
|
|
203
|
+
worktreePath: row.worktreePath, effectiveCwd, archived, subagent,
|
|
204
|
+
lineage: { parentThreadId: stringValue(lineage.parentThreadId) ?? null, relationshipToParent: stringValue(lineage.relationshipToParent) ?? null, rootThreadId: stringValue(lineage.rootThreadId) ?? null },
|
|
205
|
+
origin: originOf(projected, importMetadata, legacy.imported), bindings, historicalBindings, migration,
|
|
206
|
+
mapping: localAmbiguity || importMismatch ? "ambiguous" : mapping,
|
|
207
|
+
ambiguity, evidence, includeInReport,
|
|
208
|
+
}];
|
|
209
|
+
});
|
|
210
|
+
const byNative = new Map();
|
|
211
|
+
for (const thread of out)
|
|
212
|
+
for (const binding of [...thread.bindings, ...thread.historicalBindings]) {
|
|
213
|
+
if (binding.strength !== "strong" || !binding.nativeId)
|
|
214
|
+
continue;
|
|
215
|
+
const key = `${binding.source}\u0000${binding.nativeId}`;
|
|
216
|
+
byNative.set(key, [...(byNative.get(key) ?? []), thread]);
|
|
217
|
+
}
|
|
218
|
+
const ambiguous = new Map();
|
|
219
|
+
for (const [key, matches] of byNative) {
|
|
220
|
+
const threadIds = [...new Set(matches.map((thread) => thread.threadId))];
|
|
221
|
+
if (threadIds.length > 1)
|
|
222
|
+
ambiguous.set(key, threadIds);
|
|
223
|
+
}
|
|
224
|
+
return out.map((thread) => {
|
|
225
|
+
const keys = [...thread.bindings, ...thread.historicalBindings].filter((binding) => binding.strength === "strong" && binding.nativeId)
|
|
226
|
+
.map((binding) => `${binding.source}\u0000${binding.nativeId}`);
|
|
227
|
+
const duplicateTargets = keys.flatMap((key) => ambiguous.get(key) ?? []);
|
|
228
|
+
if (!duplicateTargets.length)
|
|
229
|
+
return thread;
|
|
230
|
+
const threadIds = [...new Set(duplicateTargets)].sort();
|
|
231
|
+
return { ...thread, mapping: "ambiguous", ambiguity: [...thread.ambiguity, `Native source binding also appears on T3 thread(s): ${threadIds.filter((id) => id !== thread.threadId).join(", ")}.`] };
|
|
232
|
+
}).filter((thread) => thread.includeInReport).map(({ includeInReport: _includeInReport, ...thread }) => thread);
|
|
233
|
+
}
|
|
234
|
+
/** Read-only evidence report. It does not compare or claim transcript equality. */
|
|
235
|
+
export async function inspectLinkedThreads(paths, options = {}) {
|
|
236
|
+
if (!existsSync(paths.dbPath))
|
|
237
|
+
throw safetyError(`T3 database not found: ${paths.dbPath}`);
|
|
238
|
+
const runtime = await inspectRuntime(paths);
|
|
239
|
+
const db = new Database(paths.dbPath, { readonly: true, fileMustExist: true });
|
|
240
|
+
try {
|
|
241
|
+
const schema = validateTargetDatabase(db);
|
|
242
|
+
const v2 = isV2Database(db);
|
|
243
|
+
const threads = buildRows(db, v2, options);
|
|
244
|
+
return {
|
|
245
|
+
schemaVersion: 1, target: paths.dbPath, migration: schema.migration, generatedAt: new Date().toISOString(),
|
|
246
|
+
provisional: runtime.live, runtime: { live: runtime.live, ...(runtime.pid ? { pid: runtime.pid } : {}), ...(runtime.origin ? { origin: runtime.origin } : {}) },
|
|
247
|
+
transcriptEquality: "not_assessed", threads,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
if (error instanceof Error && error.name === "ImporterError")
|
|
252
|
+
throw error;
|
|
253
|
+
throw compatibilityError(`Cannot inspect linked threads: ${error instanceof Error ? error.message : String(error)}`);
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
db.close();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=reconciliation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciliation.js","sourceRoot":"","sources":["../../src/target/reconciliation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CanonicalConversation, ConversationReplacePreview, ReplaceRunResult, ReplaceSelection, TargetPaths } from "../core/types.js";
|
|
2
|
+
import type { TargetOverrides } from "./config.js";
|
|
3
|
+
export interface ReplaceOptions extends TargetOverrides {
|
|
4
|
+
dryRun: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function replacementThreadId(sourceKey: string, oldThreadId: string): string;
|
|
7
|
+
export declare function replacementIdentitySeed(sourceKey: string, oldThreadId: string): string;
|
|
8
|
+
export declare function inspectConversationReplacement(conversation: CanonicalConversation, paths: TargetPaths): Promise<ConversationReplacePreview>;
|
|
9
|
+
export declare function replaceConversations(selections: ReplaceSelection[], paths: TargetPaths, options: ReplaceOptions): Promise<ReplaceRunResult>;
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
4
|
+
import { isTerminalTurn } from "../core/types.js";
|
|
5
|
+
import { checkpointForThread } from "../core/checkpoint.js";
|
|
6
|
+
import { deterministicUuid, isObject } from "../core/util.js";
|
|
7
|
+
import { safetyError, writeError } from "../core/errors.js";
|
|
8
|
+
import { listImports, recordConsolidation } from "./ledger.js";
|
|
9
|
+
import { assertT3Closed, isV2Database, validateTargetDatabase } from "./schema.js";
|
|
10
|
+
import { assertV2ThreadSettled, prepareV2Write, v2SourceConflict, v2Thread } from "./v2.js";
|
|
11
|
+
import { threadWorkingDirectory } from "./destination.js";
|
|
12
|
+
import { IMPORTER_VERSION, MAX_SAFE_IMPORT_EVENTS, acquireLock, compactThreadForImport, createBackup, event, materializeAssets, modelSelection, planThread, projectionBacklog, } from "./importer.js";
|
|
13
|
+
import { bootstrapCheckpoint, createEvent, existingProvider, findRecord, readEvents, targetId } from "./sync.js";
|
|
14
|
+
const conflictStatuses = new Set([
|
|
15
|
+
"not-imported", "active-source", "branch-replace-unsupported", "target-missing",
|
|
16
|
+
]);
|
|
17
|
+
export function replacementThreadId(sourceKey, oldThreadId) {
|
|
18
|
+
return deterministicUuid(`t3-import:replacement:${sourceKey}:${oldThreadId}`);
|
|
19
|
+
}
|
|
20
|
+
export function replacementIdentitySeed(sourceKey, oldThreadId) {
|
|
21
|
+
return `replacement:${sourceKey}:${oldThreadId}`;
|
|
22
|
+
}
|
|
23
|
+
function selectedThread(conversation) {
|
|
24
|
+
return conversation.threads.find((thread) => thread.currentBranch) ?? conversation.threads[0];
|
|
25
|
+
}
|
|
26
|
+
function runtimeRow(db, threadId) {
|
|
27
|
+
return db.prepare(`
|
|
28
|
+
SELECT provider_name providerName, provider_instance_id instanceId, adapter_key adapterKey,
|
|
29
|
+
runtime_mode runtimeMode, status, last_seen_at lastSeenAt,
|
|
30
|
+
resume_cursor_json resumeCursorJson, runtime_payload_json runtimePayloadJson
|
|
31
|
+
FROM provider_session_runtime WHERE thread_id = ?
|
|
32
|
+
`).get(threadId);
|
|
33
|
+
}
|
|
34
|
+
function isDeleted(db, threadId, events) {
|
|
35
|
+
if (isV2Database(db) && v2Thread(db, threadId)?.deletedAt)
|
|
36
|
+
return true;
|
|
37
|
+
if (events.some((row) => row.type === "thread.deleted"))
|
|
38
|
+
return true;
|
|
39
|
+
const table = db.prepare("SELECT 1 found FROM sqlite_master WHERE type='table' AND name='projection_threads'").get();
|
|
40
|
+
if (!table)
|
|
41
|
+
return false;
|
|
42
|
+
const row = db.prepare("SELECT deleted_at deletedAt FROM projection_threads WHERE thread_id = ?").get(threadId);
|
|
43
|
+
return Boolean(row?.deletedAt);
|
|
44
|
+
}
|
|
45
|
+
function replacementMarker(events) {
|
|
46
|
+
const metadata = createEvent(events)?.metadata.t3Import;
|
|
47
|
+
return isObject(metadata) ? metadata : undefined;
|
|
48
|
+
}
|
|
49
|
+
function checkpointMatches(record, thread, events) {
|
|
50
|
+
const checkpoint = record.checkpoint ?? bootstrapCheckpoint(thread, record, events)?.checkpoint;
|
|
51
|
+
if (!checkpoint || checkpoint.version !== 2 || checkpoint.turns.length !== thread.turns.length)
|
|
52
|
+
return false;
|
|
53
|
+
return JSON.stringify(checkpoint) === JSON.stringify(checkpointForThread(thread));
|
|
54
|
+
}
|
|
55
|
+
function canonicalSessionRecords(db, paths, thread, primary) {
|
|
56
|
+
if (thread.source !== "claude")
|
|
57
|
+
return [primary];
|
|
58
|
+
const records = listImports(targetId(paths), thread.source)
|
|
59
|
+
.filter((record) => record.sourceSessionId === thread.sourceSessionId
|
|
60
|
+
&& record.projectId === primary.projectId
|
|
61
|
+
&& record.isCanonical !== false)
|
|
62
|
+
.filter((record) => {
|
|
63
|
+
const events = readEvents(db, record.threadId);
|
|
64
|
+
return Boolean(createEvent(events)) && !isDeleted(db, record.threadId, events);
|
|
65
|
+
});
|
|
66
|
+
const unique = new Map(records.map((record) => [record.threadId, record]));
|
|
67
|
+
unique.set(primary.threadId, primary);
|
|
68
|
+
return [primary, ...[...unique.values()].filter((record) => record.threadId !== primary.threadId)];
|
|
69
|
+
}
|
|
70
|
+
function item(thread, status, warning, records) {
|
|
71
|
+
const record = records?.[0];
|
|
72
|
+
const consolidationWarning = records && records.length > 1
|
|
73
|
+
? `Consolidating ${records.length} previously imported Claude fragments into one canonical task.`
|
|
74
|
+
: undefined;
|
|
75
|
+
return {
|
|
76
|
+
source: thread.source, sourceId: thread.sourceSessionId, sourceKey: thread.sourceKey,
|
|
77
|
+
status, ...(record ? { oldThreadId: record.threadId, projectId: record.projectId } : {}),
|
|
78
|
+
title: thread.title, turns: thread.turns.length, events: 0, attachments: 0,
|
|
79
|
+
resumeTransferred: false,
|
|
80
|
+
...(records && records.length > 1 ? { oldThreadIds: records.map((entry) => entry.threadId) } : {}),
|
|
81
|
+
warnings: [...thread.warnings, ...(consolidationWarning ? [consolidationWarning] : []), ...(warning ? [warning] : [])],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function buildPlan(db, paths, conversation, dryRun) {
|
|
85
|
+
const source = selectedThread(conversation);
|
|
86
|
+
if (!source)
|
|
87
|
+
throw writeError(`Conversation ${conversation.summary.id} has no importable task.`);
|
|
88
|
+
const active = source.turns.some((turn) => !isTerminalTurn(turn)) || (source.ignoredInProgressTurns ?? 0) > 0;
|
|
89
|
+
const thread = compactThreadForImport({ ...source, turns: source.turns.filter(isTerminalTurn) });
|
|
90
|
+
const match = findRecord(db, paths, thread);
|
|
91
|
+
if (!match.record) {
|
|
92
|
+
return { conversation, thread, result: item(thread, match.stale ? "target-missing" : "not-imported", match.stale ? "The canonical ledger entry is stale because its T3 task is missing." : "Conversation has not been imported into this T3 database.") };
|
|
93
|
+
}
|
|
94
|
+
const record = match.record;
|
|
95
|
+
if (isV2Database(db)) {
|
|
96
|
+
assertV2ThreadSettled(db, record.threadId);
|
|
97
|
+
const conflict = v2SourceConflict(db, record.threadId, thread);
|
|
98
|
+
if (conflict)
|
|
99
|
+
throw safetyError(conflict);
|
|
100
|
+
}
|
|
101
|
+
const oldRecords = canonicalSessionRecords(db, paths, thread, record);
|
|
102
|
+
const oldEvents = readEvents(db, record.threadId);
|
|
103
|
+
if (!createEvent(oldEvents) || isDeleted(db, record.threadId, oldEvents)) {
|
|
104
|
+
return { conversation, thread, oldRecord: record, oldRecords, result: item(thread, "target-missing", "The canonical T3 task is missing or deleted.", oldRecords) };
|
|
105
|
+
}
|
|
106
|
+
if (active)
|
|
107
|
+
return { conversation, thread, oldRecord: record, oldRecords, result: item(thread, "active-source", "The provider conversation has an active or indeterminate turn. Retry replacement after it settles.", oldRecords) };
|
|
108
|
+
if (thread.source === "claude" && conversation.summary.branches !== 1) {
|
|
109
|
+
return { conversation, thread, oldRecord: record, oldRecords, result: item(thread, "branch-replace-unsupported", "Claude replacement requires exactly one non-sidechain leaf.", oldRecords) };
|
|
110
|
+
}
|
|
111
|
+
const marker = replacementMarker(oldEvents);
|
|
112
|
+
if (oldRecords.length === 1 && marker?.canonical === true && typeof marker.replacesThreadId === "string" && checkpointMatches(record, thread, oldEvents)) {
|
|
113
|
+
return { conversation, thread, oldRecord: record, oldRecords, result: item(thread, "already-current", undefined, oldRecords) };
|
|
114
|
+
}
|
|
115
|
+
const newThreadId = replacementThreadId(thread.sourceKey, record.threadId);
|
|
116
|
+
const seed = replacementIdentitySeed(thread.sourceKey, record.threadId);
|
|
117
|
+
const provider = existingProvider(db, thread, record, oldEvents);
|
|
118
|
+
const runtime = runtimeRow(db, record.threadId);
|
|
119
|
+
const resumable = record.resumable && Boolean(runtime?.resumeCursorJson ?? thread.resumeCursor);
|
|
120
|
+
const metadata = {
|
|
121
|
+
t3Import: {
|
|
122
|
+
schemaVersion: 1,
|
|
123
|
+
source: thread.source,
|
|
124
|
+
sourceSessionId: thread.sourceSessionId,
|
|
125
|
+
sourceKey: thread.sourceKey,
|
|
126
|
+
identitySeed: seed,
|
|
127
|
+
canonical: true,
|
|
128
|
+
replacesThreadId: record.threadId,
|
|
129
|
+
...(oldRecords.length > 1 ? { replacesThreadIds: oldRecords.map((entry) => entry.threadId) } : {}),
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
const planned = planThread(thread, newThreadId, record.projectId, paths, provider, seed, undefined, resumable, conversation.fingerprint, 0, true, metadata);
|
|
133
|
+
const workspace = threadWorkingDirectory(db, record.threadId, thread.workspace);
|
|
134
|
+
const created = planned.events.find((entry) => entry.type === "thread.created");
|
|
135
|
+
if (created)
|
|
136
|
+
created.payload.worktreePath = workspace.worktreePath;
|
|
137
|
+
if (created && isV2Database(db)) {
|
|
138
|
+
const previous = v2Thread(db, record.threadId);
|
|
139
|
+
created.payload.runtimeMode = previous?.runtimeMode ?? "full-access";
|
|
140
|
+
created.payload.interactionMode = previous?.interactionMode ?? "default";
|
|
141
|
+
}
|
|
142
|
+
const deletedAt = new Date().toISOString();
|
|
143
|
+
const deletions = oldRecords.flatMap((oldRecord, index) => {
|
|
144
|
+
const events = oldRecord.threadId === record.threadId ? oldEvents : readEvents(db, oldRecord.threadId);
|
|
145
|
+
if (!createEvent(events) || isDeleted(db, oldRecord.threadId, events))
|
|
146
|
+
return [];
|
|
147
|
+
return [{
|
|
148
|
+
record: oldRecord,
|
|
149
|
+
event: event(seed, oldRecords.length === 1 ? "replaced-thread.deleted" : `replaced-thread.${index}.deleted`, "thread", oldRecord.threadId, "thread.deleted", deletedAt, "client", {
|
|
150
|
+
threadId: oldRecord.threadId, deletedAt,
|
|
151
|
+
}, { replacedByThreadId: newThreadId, source: "t3-import" }),
|
|
152
|
+
}];
|
|
153
|
+
});
|
|
154
|
+
const result = {
|
|
155
|
+
...item(thread, dryRun ? "dry-run" : "replaced", undefined, oldRecords),
|
|
156
|
+
newThreadId, events: planned.events.length + deletions.length, attachments: planned.assets.length,
|
|
157
|
+
resumeTransferred: resumable,
|
|
158
|
+
};
|
|
159
|
+
return { conversation, thread, oldRecord: record, oldRecords, planned, deletions, ...(runtime ? { runtime } : {}), result };
|
|
160
|
+
}
|
|
161
|
+
function aggregate(results, dryRun) {
|
|
162
|
+
if (dryRun && results.some((result) => result.status === "dry-run"))
|
|
163
|
+
return "dry-run";
|
|
164
|
+
const conflict = results.some((result) => conflictStatuses.has(result.status));
|
|
165
|
+
const replaced = results.some((result) => result.status === "replaced");
|
|
166
|
+
if (conflict && replaced)
|
|
167
|
+
return "mixed";
|
|
168
|
+
if (conflict)
|
|
169
|
+
return "conflict";
|
|
170
|
+
if (replaced)
|
|
171
|
+
return "replaced";
|
|
172
|
+
return "already-current";
|
|
173
|
+
}
|
|
174
|
+
export async function inspectConversationReplacement(conversation, paths) {
|
|
175
|
+
const db = new Database(paths.dbPath, { readonly: true, fileMustExist: true });
|
|
176
|
+
try {
|
|
177
|
+
validateTargetDatabase(db);
|
|
178
|
+
const plan = buildPlan(db, paths, conversation, false);
|
|
179
|
+
const status = plan.result.status === "replaced" ? "replaceable" : plan.result.status;
|
|
180
|
+
return {
|
|
181
|
+
sourceId: plan.result.sourceId, status,
|
|
182
|
+
selectable: status === "replaceable", previouslyImported: status !== "not-imported",
|
|
183
|
+
turns: plan.result.turns, events: plan.result.events, attachments: plan.result.attachments,
|
|
184
|
+
...(plan.result.oldThreadId ? { oldThreadId: plan.result.oldThreadId } : {}),
|
|
185
|
+
...(plan.result.oldThreadIds ? { oldThreadIds: plan.result.oldThreadIds } : {}),
|
|
186
|
+
...(plan.result.newThreadId ? { newThreadId: plan.result.newThreadId } : {}),
|
|
187
|
+
warnings: plan.result.warnings,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
db.close();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export async function replaceConversations(selections, paths, options) {
|
|
195
|
+
if (!existsSync(paths.dbPath))
|
|
196
|
+
throw safetyError(`T3 database not found: ${paths.dbPath}`);
|
|
197
|
+
await assertT3Closed(paths);
|
|
198
|
+
const releaseLock = options.dryRun ? () => { } : acquireLock(paths);
|
|
199
|
+
const db = new Database(paths.dbPath, { readonly: options.dryRun, fileMustExist: true });
|
|
200
|
+
let backup = null;
|
|
201
|
+
let v2Committed = false;
|
|
202
|
+
const warnings = [];
|
|
203
|
+
try {
|
|
204
|
+
const schema = validateTargetDatabase(db);
|
|
205
|
+
const plans = selections.map((selection) => buildPlan(db, paths, selection.conversation, options.dryRun));
|
|
206
|
+
const writable = plans.filter((plan) => plan.planned && plan.deletions?.length && plan.oldRecord && plan.oldRecords?.length);
|
|
207
|
+
const v2Plans = isV2Database(db) ? writable.flatMap((plan) => [plan.planned, ...plan.deletions.map((deletion) => ({ ...plan.planned, threadId: deletion.record.threadId, events: [deletion.event], resumable: false }))]) : [];
|
|
208
|
+
const v2Write = v2Plans.length ? await prepareV2Write(db, v2Plans) : undefined;
|
|
209
|
+
if (v2Write)
|
|
210
|
+
for (const plan of writable)
|
|
211
|
+
plan.result.events = plan.planned.targetEventCount + plan.deletions.length;
|
|
212
|
+
if (writable.length > 0) {
|
|
213
|
+
const backlog = projectionBacklog(db);
|
|
214
|
+
if (backlog.backlog > 0)
|
|
215
|
+
throw safetyError(`T3 has ${backlog.backlog} unprojected event${backlog.backlog === 1 ? "" : "s"}. Open T3 and let it finish loading before replacing conversations.`);
|
|
216
|
+
const count = writable.reduce((sum, plan) => sum + (plan.planned.targetEventCount ?? plan.planned.events.length) + plan.deletions.length, 0);
|
|
217
|
+
if (count > MAX_SAFE_IMPORT_EVENTS)
|
|
218
|
+
throw safetyError(`Replacement would append ${count} events, exceeding the safe one-launch limit of ${MAX_SAFE_IMPORT_EVENTS}. Replace fewer conversations at a time.`);
|
|
219
|
+
}
|
|
220
|
+
if (options.dryRun || writable.length === 0) {
|
|
221
|
+
const results = plans.map((plan) => plan.result);
|
|
222
|
+
return { schemaVersion: 1, status: aggregate(results, options.dryRun), target: paths.dbPath, migration: schema.migration, backup: null, results, warnings, hasConflicts: results.some((result) => conflictStatuses.has(result.status)) };
|
|
223
|
+
}
|
|
224
|
+
backup = createBackup(db, paths);
|
|
225
|
+
mkdirSync(paths.attachmentsDir, { recursive: true });
|
|
226
|
+
const assets = materializeAssets(writable.map((plan) => plan.planned), paths, randomUUID());
|
|
227
|
+
const ledgerUpdates = [];
|
|
228
|
+
try {
|
|
229
|
+
const v2Written = v2Write ? await v2Write.write() : undefined;
|
|
230
|
+
v2Committed = Boolean(v2Written);
|
|
231
|
+
const nextVersion = db.prepare("SELECT COALESCE(MAX(stream_version), -1) + 1 nextVersion FROM orchestration_events WHERE aggregate_kind = ? AND stream_id = ?");
|
|
232
|
+
const insert = db.prepare(`INSERT INTO orchestration_events (event_id, aggregate_kind, stream_id, stream_version, event_type, occurred_at, command_id, causation_event_id, correlation_id, actor_kind, payload_json, metadata_json) VALUES (?, ?, ?, ?, ?, ?, NULL, NULL, NULL, ?, ?, ?)`);
|
|
233
|
+
const deleteRuntime = db.prepare("DELETE FROM provider_session_runtime WHERE thread_id = ?");
|
|
234
|
+
const upsertRuntime = db.prepare(`
|
|
235
|
+
INSERT INTO provider_session_runtime (thread_id, provider_name, provider_instance_id, adapter_key, runtime_mode, status, last_seen_at, resume_cursor_json, runtime_payload_json)
|
|
236
|
+
VALUES (?, ?, ?, ?, ?, 'stopped', ?, ?, ?)
|
|
237
|
+
ON CONFLICT(thread_id) DO UPDATE SET provider_name=excluded.provider_name, provider_instance_id=excluded.provider_instance_id, adapter_key=excluded.adapter_key, runtime_mode=excluded.runtime_mode, status='stopped', last_seen_at=excluded.last_seen_at, resume_cursor_json=excluded.resume_cursor_json, runtime_payload_json=excluded.runtime_payload_json
|
|
238
|
+
`);
|
|
239
|
+
if (!v2Written)
|
|
240
|
+
db.exec("BEGIN IMMEDIATE");
|
|
241
|
+
try {
|
|
242
|
+
for (const plan of writable) {
|
|
243
|
+
const planned = plan.planned;
|
|
244
|
+
const old = plan.oldRecord;
|
|
245
|
+
const oldRecords = plan.oldRecords;
|
|
246
|
+
let first = Number.MAX_SAFE_INTEGER;
|
|
247
|
+
let last = 0;
|
|
248
|
+
if (v2Written) {
|
|
249
|
+
const sequences = [...v2Written].filter(([candidate]) => candidate === planned || plan.deletions.some((deletion) => deletion.record.threadId === candidate.threadId)).map(([, sequence]) => sequence);
|
|
250
|
+
first = Math.min(...sequences.map((sequence) => sequence.first));
|
|
251
|
+
last = Math.max(...sequences.map((sequence) => sequence.last));
|
|
252
|
+
}
|
|
253
|
+
let newVersion = v2Written ? 0 : nextVersion.get("thread", planned.threadId).nextVersion;
|
|
254
|
+
for (const row of v2Written ? [] : planned.events) {
|
|
255
|
+
const inserted = insert.run(row.eventId, row.aggregateKind, row.streamId, newVersion++, row.type, row.occurredAt, row.actorKind, JSON.stringify(row.payload), JSON.stringify(row.metadata));
|
|
256
|
+
const sequence = Number(inserted.lastInsertRowid);
|
|
257
|
+
first = Math.min(first, sequence);
|
|
258
|
+
last = Math.max(last, sequence);
|
|
259
|
+
}
|
|
260
|
+
for (const deletion of v2Written ? [] : plan.deletions) {
|
|
261
|
+
const row = deletion.event;
|
|
262
|
+
const oldVersion = nextVersion.get("thread", deletion.record.threadId).nextVersion;
|
|
263
|
+
const deleted = insert.run(row.eventId, row.aggregateKind, row.streamId, oldVersion, row.type, row.occurredAt, row.actorKind, JSON.stringify(row.payload), JSON.stringify(row.metadata));
|
|
264
|
+
const deleteSequence = Number(deleted.lastInsertRowid);
|
|
265
|
+
first = Math.min(first, deleteSequence);
|
|
266
|
+
last = Math.max(last, deleteSequence);
|
|
267
|
+
}
|
|
268
|
+
if (!v2Written && plan.result.resumeTransferred) {
|
|
269
|
+
const runtime = plan.runtime;
|
|
270
|
+
let cursor;
|
|
271
|
+
try {
|
|
272
|
+
cursor = runtime?.resumeCursorJson ? JSON.parse(runtime.resumeCursorJson) : planned.thread.resumeCursor;
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
cursor = planned.thread.resumeCursor;
|
|
276
|
+
}
|
|
277
|
+
if (cursor && planned.thread.source === "claude")
|
|
278
|
+
cursor = { ...cursor, threadId: planned.threadId };
|
|
279
|
+
upsertRuntime.run(planned.threadId, runtime?.providerName ?? planned.provider.providerName, runtime?.instanceId ?? planned.provider.instanceId, runtime?.adapterKey ?? planned.provider.adapterKey, runtime?.runtimeMode ?? "full-access", planned.thread.updatedAt, cursor ? JSON.stringify(cursor) : null, runtime?.runtimePayloadJson ?? JSON.stringify({ cwd: planned.thread.workspace, model: planned.provider.model, activeTurnId: null, lastError: null, modelSelection: modelSelection(planned.provider), lastRuntimeEvent: `t3-import.replace.${planned.thread.source}`, lastRuntimeEventAt: planned.thread.updatedAt }));
|
|
280
|
+
}
|
|
281
|
+
if (!v2Written)
|
|
282
|
+
for (const oldRecord of oldRecords)
|
|
283
|
+
deleteRuntime.run(oldRecord.threadId);
|
|
284
|
+
plan.result.sequence = { first, last };
|
|
285
|
+
const replacedAt = new Date().toISOString();
|
|
286
|
+
ledgerUpdates.push({ old: oldRecords, next: {
|
|
287
|
+
targetId: targetId(paths), source: planned.thread.source, sourceSessionId: planned.thread.sourceSessionId,
|
|
288
|
+
sourceKey: planned.thread.sourceKey, sourceFingerprint: plan.conversation.fingerprint,
|
|
289
|
+
projectId: planned.projectId, threadId: planned.threadId, importedAt: replacedAt,
|
|
290
|
+
importerVersion: IMPORTER_VERSION, migration: schema.migration, firstSequence: first,
|
|
291
|
+
lastSequence: last, resumable: plan.result.resumeTransferred, backupPath: backup,
|
|
292
|
+
warnings: plan.result.warnings, identitySeed: replacementIdentitySeed(planned.thread.sourceKey, old.threadId),
|
|
293
|
+
currentSourceKey: planned.thread.sourceKey, ...(planned.thread.leafId ? { sourceLeafId: planned.thread.leafId } : {}),
|
|
294
|
+
sourceTitle: planned.thread.title, checkpoint: checkpointForThread(planned.thread), syncedAt: replacedAt,
|
|
295
|
+
isCanonical: true, replacedAt,
|
|
296
|
+
} });
|
|
297
|
+
}
|
|
298
|
+
if (!v2Written)
|
|
299
|
+
db.exec("COMMIT");
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (!v2Written)
|
|
303
|
+
db.exec("ROLLBACK");
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
assets.cleanup();
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
if (!v2Committed)
|
|
310
|
+
for (const path of assets.created)
|
|
311
|
+
rmSync(path, { force: true });
|
|
312
|
+
assets.cleanup();
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
const integrity = String(db.pragma("integrity_check", { simple: true }));
|
|
316
|
+
if (integrity !== "ok")
|
|
317
|
+
throw writeError(`Post-replacement integrity check failed: ${integrity}`);
|
|
318
|
+
for (const update of ledgerUpdates) {
|
|
319
|
+
try {
|
|
320
|
+
recordConsolidation(update.old, update.next);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
warnings.push(`Replacement succeeded, but the external ledger could not be updated: ${error instanceof Error ? error.message : String(error)}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const results = plans.map((plan) => plan.result);
|
|
327
|
+
return { schemaVersion: 1, status: aggregate(results, false), target: paths.dbPath, migration: schema.migration, backup, results, warnings, hasConflicts: results.some((result) => conflictStatuses.has(result.status)) };
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
if (v2Committed)
|
|
331
|
+
throw writeError(`V2 replacement committed, but post-write verification or bookkeeping failed: ${error instanceof Error ? error.message : String(error)}. Attachments were retained. Inspect the replacement and recover the external ledger before further writes; do not repeat replacement blindly.`, error);
|
|
332
|
+
if (error instanceof Error && error.name === "ImporterError")
|
|
333
|
+
throw error;
|
|
334
|
+
throw writeError(`Replacement failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
|
335
|
+
}
|
|
336
|
+
finally {
|
|
337
|
+
db.close();
|
|
338
|
+
releaseLock();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=replace.js.map
|