@turns/dev 0.0.26 → 0.0.28
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/local-session-sources.js +43 -0
- package/dist/upload-sessions.js +25 -1
- package/package.json +1 -1
|
@@ -964,6 +964,45 @@ function firstRecords(descriptor, limit = 32, maxBytes = 1024 * 1024) {
|
|
|
964
964
|
return [];
|
|
965
965
|
}
|
|
966
966
|
}
|
|
967
|
+
/** Claude copies a backgrounded session into a new transcript and ends the
|
|
968
|
+
* old one with `{"type":"continued-in","continuedInSessionId":...}`, followed
|
|
969
|
+
* only by small bookkeeping rows. Read just the file's tail for that marker. */
|
|
970
|
+
function claudeContinuedInSessionId(file) {
|
|
971
|
+
let handle;
|
|
972
|
+
try {
|
|
973
|
+
const stat = fs.statSync(file);
|
|
974
|
+
const bytesToRead = Math.min(stat.size, 16 * 1024);
|
|
975
|
+
handle = fs.openSync(file, 'r');
|
|
976
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
977
|
+
const bytesRead = fs.readSync(handle, buffer, 0, buffer.length, stat.size - bytesToRead);
|
|
978
|
+
const text = buffer.subarray(0, bytesRead).toString('utf8');
|
|
979
|
+
if (!text.includes('"continued-in"'))
|
|
980
|
+
return '';
|
|
981
|
+
let continuedIn = '';
|
|
982
|
+
for (const line of text.split('\n')) {
|
|
983
|
+
if (!line.includes('"continued-in"'))
|
|
984
|
+
continue;
|
|
985
|
+
try {
|
|
986
|
+
const record = jsonObject(JSON.parse(line));
|
|
987
|
+
const id = String(record?.continuedInSessionId || '').trim();
|
|
988
|
+
if (record?.type === 'continued-in' && /^[0-9a-f-]{8,64}$/iu.test(id))
|
|
989
|
+
continuedIn = id;
|
|
990
|
+
}
|
|
991
|
+
catch { }
|
|
992
|
+
}
|
|
993
|
+
return continuedIn;
|
|
994
|
+
}
|
|
995
|
+
catch {
|
|
996
|
+
return '';
|
|
997
|
+
}
|
|
998
|
+
finally {
|
|
999
|
+
try {
|
|
1000
|
+
if (handle !== undefined)
|
|
1001
|
+
fs.closeSync(handle);
|
|
1002
|
+
}
|
|
1003
|
+
catch { }
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
967
1006
|
/** Native identity and parentage carried by a bounded transcript prefix. */
|
|
968
1007
|
export function nativeDescriptorLineage(descriptor) {
|
|
969
1008
|
const sourcePath = String(descriptor?.sourcePath || descriptor?.file || '').replaceAll('\\', '/');
|
|
@@ -1042,10 +1081,14 @@ export function nativeDescriptorLineage(descriptor) {
|
|
|
1042
1081
|
}
|
|
1043
1082
|
if (parentNativeSessionId)
|
|
1044
1083
|
isSubagent = true;
|
|
1084
|
+
const continuedInNativeSessionId = descriptor?.harness === 'claude' && !isSubagent && descriptor?.file
|
|
1085
|
+
? claudeContinuedInSessionId(String(descriptor.file))
|
|
1086
|
+
: '';
|
|
1045
1087
|
const value = {
|
|
1046
1088
|
nativeSessionId,
|
|
1047
1089
|
parentNativeSessionId,
|
|
1048
1090
|
isSubagent,
|
|
1091
|
+
...(continuedInNativeSessionId ? { continuedInNativeSessionId } : {}),
|
|
1049
1092
|
...(structural?.uploadable === false ? { uploadable: false } : {}),
|
|
1050
1093
|
...(parentSourcePath ? { parentSourcePath } : {}),
|
|
1051
1094
|
...(claudeMetadata?.description ? { nativeTitle: claudeMetadata.description } : {}),
|
package/dist/upload-sessions.js
CHANGED
|
@@ -317,6 +317,19 @@ export function linkNativeSessionParents(machine, descriptors) {
|
|
|
317
317
|
candidates.push(record.descriptor);
|
|
318
318
|
byNativeId.set(key, candidates);
|
|
319
319
|
}
|
|
320
|
+
// A continued transcript names its successor; key it by the successor's
|
|
321
|
+
// store directory so equal native IDs in other projects never match.
|
|
322
|
+
const nativeDirectory = (descriptor) => path.posix.dirname(String(descriptor?.sourcePath || '').replaceAll('\\', '/'));
|
|
323
|
+
const predecessorsBySuccessor = new Map();
|
|
324
|
+
for (const record of records) {
|
|
325
|
+
const successorId = record.lineage.continuedInNativeSessionId;
|
|
326
|
+
if (!successorId || successorId === record.lineage.nativeSessionId)
|
|
327
|
+
continue;
|
|
328
|
+
const key = `${record.descriptor.harness}\0${nativeDirectory(record.descriptor)}\0${successorId}`;
|
|
329
|
+
const candidates = predecessorsBySuccessor.get(key) || [];
|
|
330
|
+
candidates.push(record.descriptor);
|
|
331
|
+
predecessorsBySuccessor.set(key, candidates);
|
|
332
|
+
}
|
|
320
333
|
const grokParentsByChildId = new Map();
|
|
321
334
|
for (const record of records) {
|
|
322
335
|
if (record.descriptor.harness !== 'grok')
|
|
@@ -367,6 +380,10 @@ export function linkNativeSessionParents(machine, descriptors) {
|
|
|
367
380
|
// native upload until its store exposes enough lineage to nest it safely.
|
|
368
381
|
if (isSubagent && !parentSourcePath)
|
|
369
382
|
return [];
|
|
383
|
+
const predecessors = !isSubagent && lineage.nativeSessionId
|
|
384
|
+
? predecessorsBySuccessor.get(`${descriptor.harness}\0${nativeDirectory(descriptor)}\0${lineage.nativeSessionId}`) || []
|
|
385
|
+
: [];
|
|
386
|
+
const predecessor = predecessors.length === 1 ? predecessors[0] : null;
|
|
370
387
|
return [
|
|
371
388
|
{
|
|
372
389
|
...descriptor,
|
|
@@ -379,6 +396,9 @@ export function linkNativeSessionParents(machine, descriptors) {
|
|
|
379
396
|
parentSessionId: stableNativeSessionId(machine, descriptor.harness, parentSourcePath),
|
|
380
397
|
}
|
|
381
398
|
: {}),
|
|
399
|
+
...(predecessor
|
|
400
|
+
? { supersedesSessionId: stableNativeSessionId(machine, descriptor.harness, predecessor.sourcePath) }
|
|
401
|
+
: {}),
|
|
382
402
|
},
|
|
383
403
|
];
|
|
384
404
|
});
|
|
@@ -391,6 +411,8 @@ export function nativeDescriptorFingerprint(descriptor) {
|
|
|
391
411
|
String(descriptor.parentSessionId || ''),
|
|
392
412
|
NATIVE_DESCRIPTOR_VERSION,
|
|
393
413
|
nativeDescriptorMetadataFingerprint(descriptor),
|
|
414
|
+
// Appended only when present so unrelated sessions keep their fingerprint.
|
|
415
|
+
...(descriptor.supersedesSessionId ? [String(descriptor.supersedesSessionId)] : []),
|
|
394
416
|
]);
|
|
395
417
|
}
|
|
396
418
|
function nativeDescriptorMetadataFingerprint(descriptor) {
|
|
@@ -994,6 +1016,7 @@ export async function uploadNativeSession(rpc, machine, descriptor, cursor = {},
|
|
|
994
1016
|
...(descriptor.readOnly === true ? { readOnly: true } : {}),
|
|
995
1017
|
...(agentSessionId ? { agentSessionId } : {}),
|
|
996
1018
|
...(descriptor.parentSessionId ? { parentSessionId: descriptor.parentSessionId } : {}),
|
|
1019
|
+
...(descriptor.supersedesSessionId ? { supersedesSessionId: descriptor.supersedesSessionId } : {}),
|
|
997
1020
|
},
|
|
998
1021
|
});
|
|
999
1022
|
if (opened.type === 'session_tombstoned') {
|
|
@@ -1592,7 +1615,8 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1592
1615
|
Number(previous?.[1]) === Number(descriptor.mtimeMs) &&
|
|
1593
1616
|
String(previous?.[3] || '') === String(descriptor.parentSessionId || '') &&
|
|
1594
1617
|
Number(previous?.[4]) === NATIVE_DESCRIPTOR_VERSION &&
|
|
1595
|
-
String(previous?.[5] || '') === nativeDescriptorMetadataFingerprint(descriptor)
|
|
1618
|
+
String(previous?.[5] || '') === nativeDescriptorMetadataFingerprint(descriptor) &&
|
|
1619
|
+
String(previous?.[6] || '') === String(descriptor.supersedesSessionId || '')) {
|
|
1596
1620
|
fingerprint = previousFingerprint;
|
|
1597
1621
|
}
|
|
1598
1622
|
}
|