@volter/supercode-interchange 0.1.10 → 0.1.12
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/file-io.mjs +8 -2
- package/jsonl.mjs +6 -2
- package/package.json +1 -1
package/file-io.mjs
CHANGED
|
@@ -3,6 +3,10 @@ import { parseNativeJson } from './native-json.mjs';
|
|
|
3
3
|
|
|
4
4
|
const stale = (path) => Object.assign(new Error(`${path} changed during its metadata read; previous index rows are retained`), { code: 'ESTALE' });
|
|
5
5
|
const same = (a, b) => a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.mtimeNs === b.mtimeNs;
|
|
6
|
+
// A file its writer appended to while it was read is the same file with more after the bytes read: the facts read are
|
|
7
|
+
// those of the open's size, and the next read takes the rest. A live transcript is written more often than a large
|
|
8
|
+
// read takes, so refusing an append would refuse every read of it.
|
|
9
|
+
const sameOrAppended = (before, now) => same(before, now) || before.dev === now.dev && before.ino === now.ino && now.size > before.size;
|
|
6
10
|
|
|
7
11
|
export function fileFingerprint(info) {
|
|
8
12
|
return { device: String(info.dev), inode: String(info.ino), size: String(info.size), mtime_ns: String(info.mtimeNs) };
|
|
@@ -19,7 +23,9 @@ export function appendableFingerprint(previous, current) {
|
|
|
19
23
|
|
|
20
24
|
/** Read one open identity; verify both the handle and its current path before
|
|
21
25
|
* returning facts for a transaction. A changed path never commits new offsets
|
|
22
|
-
* beside facts read from its predecessor
|
|
26
|
+
* beside facts read from its predecessor: a file replaced, truncated or rewritten
|
|
27
|
+
* in place is stale, while one only appended to answers for the size it was
|
|
28
|
+
* opened at. The caller owns failure retry. */
|
|
23
29
|
export async function withSessionFile(path, work, metrics = {}) {
|
|
24
30
|
const handle = await open(path, 'r');
|
|
25
31
|
metrics.files_opened = (metrics.files_opened ?? 0) + 1;
|
|
@@ -51,7 +57,7 @@ export async function withSessionFile(path, work, metrics = {}) {
|
|
|
51
57
|
const after = await handle.stat({ bigint: true });
|
|
52
58
|
let current;
|
|
53
59
|
try { current = await stat(path, { bigint: true }); } catch { throw stale(path); }
|
|
54
|
-
if (!
|
|
60
|
+
if (!sameOrAppended(before, after) || !sameOrAppended(before, current) || current.size < after.size) throw stale(path);
|
|
55
61
|
return { result, fingerprint: reader.fingerprint, size, modified_ms: reader.modified_ms };
|
|
56
62
|
} finally { await handle.close(); }
|
|
57
63
|
}
|
package/jsonl.mjs
CHANGED
|
@@ -30,13 +30,17 @@ async function readHeader(reader, harness) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async function descriptor(path, harness, reader, header, facts, nativeTopic) {
|
|
33
|
-
|
|
33
|
+
// A Claude subagent's transcript (`<session>/subagents/agent-<id>.jsonl`) carries its parent's sessionId in every
|
|
34
|
+
// record: named by that id it would share its parent's index key, replace the parent's row and, as a child, drop
|
|
35
|
+
// the parent from discovery. It is named by its own file, under its parent.
|
|
36
|
+
const subagentOf = harness === 'claude-code' && basename(dirname(path)) === 'subagents' ? basename(dirname(dirname(path))) : null;
|
|
37
|
+
const sessionId = subagentOf ? basename(path, extname(path)) : header.session_id ?? basename(path, extname(path));
|
|
34
38
|
let title = header.title ?? header.topic;
|
|
35
39
|
if (title == null && harness === 'codex') {
|
|
36
40
|
if (typeof nativeTopic !== 'function') throw new Error('Codex metadata requires its native thread-name source');
|
|
37
41
|
title = await nativeTopic(path, sessionId);
|
|
38
42
|
}
|
|
39
|
-
const parent = header.parent_session_id ??
|
|
43
|
+
const parent = header.parent_session_id ?? subagentOf;
|
|
40
44
|
return finalizeNouns({
|
|
41
45
|
locator: { harness, session_id: sessionId, storage: { kind: 'file', path } },
|
|
42
46
|
cwd: header.cwd, title, updated_at_ms: facts.last_turn_ms ?? reader.modified_ms,
|
package/package.json
CHANGED