@volter/supercode-interchange 0.1.21 → 0.1.22

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/discovery.mjs CHANGED
@@ -10,7 +10,7 @@ export async function enrichDescriptor(query, descriptor, { codexTopics, metrics
10
10
  const row = { ...descriptor };
11
11
  const take = async (read) => {
12
12
  try { return await read(); }
13
- catch (error) { previewFailure?.(row.locator, error); return []; }
13
+ catch (error) { if (metrics) metrics.preview_failures = (metrics.preview_failures ?? 0) + 1; previewFailure?.(row.locator, error); return []; }
14
14
  };
15
15
  if (query.include_topic_candidates) {
16
16
  const history = row.locator.harness === 'codex' ? codexTopics?.get(row.locator.session_id) : null;
@@ -30,8 +30,19 @@ export async function enrichDescriptor(query, descriptor, { codexTopics, metrics
30
30
  export const readsPreviews = (query) => query.include_latest_candidates !== false || Boolean(query.include_topic_candidates);
31
31
 
32
32
  export async function enrichIndexPage(query, rows, options) {
33
- const result = [];
34
- for (const row of rows) result.push(await enrichDescriptor(query, row, options));
33
+ // A page used to await every open/read/stat/close before starting the next row. Keep a small number of
34
+ // independent reads in flight, preserve row order and retain every fingerprint/failure check. Never fan out
35
+ // with Promise.all(rows): a whole-index request can contain thousands of transcripts and expanded tails.
36
+ const result = new Array(rows.length);
37
+ let next = 0;
38
+ const read = async () => {
39
+ while (next < rows.length) {
40
+ const at = next++;
41
+ result[at] = await enrichDescriptor(query, rows[at], options);
42
+ }
43
+ };
44
+ const finished = await Promise.allSettled(Array.from({ length: Math.min(4, rows.length) }, read));
45
+ for (const worker of finished) if (worker.status === 'rejected') throw worker.reason;
35
46
  return result;
36
47
  }
37
48
 
@@ -46,9 +57,10 @@ export function namesWholeSessionId(query) {
46
57
  * title supplier chooses one id by source mtime, before applying the other
47
58
  * query filters, as native claude_session_named does. Neither is caller proof. */
48
59
  export async function discoverFromIndex(query, descriptors, options = {}) {
60
+ const step = options.step ?? ((_name, work) => work());
49
61
  const raw = [...descriptors];
50
- if (query.search_previews) return previewPage(query, raw, options);
51
- const page = projectMetadataPage(query, raw, options);
62
+ if (query.search_previews) return step('preview search', () => previewPage(query, raw, options));
63
+ const page = await step('metadata projection', () => projectMetadataPage(query, raw, options));
52
64
  const name = queryName(query);
53
65
  if (query.cursor == null && name) {
54
66
  const selected = query.harnesses;
@@ -78,7 +90,7 @@ export async function discoverFromIndex(query, descriptors, options = {}) {
78
90
  }
79
91
  // Keep the native receipt from projection: its counts precede the exact-name
80
92
  // post-filter and late-name fallback. Do not silently rewrite that contract.
81
- page.sessions = await enrichIndexPage(query, page.sessions, options);
93
+ page.sessions = await step('preview reads', () => enrichIndexPage(query, page.sessions, options));
82
94
  return page;
83
95
  }
84
96
 
package/index.d.ts CHANGED
@@ -108,6 +108,8 @@ export interface DiscoveryEnrichmentOptions extends MetadataProjectionOptions {
108
108
  previewFailure?: (locator: SessionLocator, error: Error) => void;
109
109
  liveClaudeName?: (name: string) => Promise<{ id: string; name: string } | null>;
110
110
  lateClaudeName?: (name: string) => Promise<string | null>;
111
+ /** Optional request-local stage timing; it must preserve the work's value and errors. */
112
+ step?: <T>(name: string, work: () => T | Promise<T>) => T | Promise<T>;
111
113
  }
112
114
  export function enrichDescriptor(query: DiscoveryQuery, descriptor: SessionDescriptor, options?: DiscoveryEnrichmentOptions): Promise<SessionDescriptor>;
113
115
  export function readsPreviews(query: DiscoveryQuery): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-interchange",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "description": "Read-only harness metadata, transcript codecs and session projections for Supercode",
6
6
  "license": "MIT OR Apache-2.0",
package/previews.mjs CHANGED
@@ -66,6 +66,30 @@ function codexMessage(native) {
66
66
 
67
67
  function parseLine(text) { try { return parseNativeJson(text); } catch { return null; } }
68
68
 
69
+ // Walk boundaries only as far as the eight candidates need. Splitting a validated 512 KiB/4 MiB window built
70
+ // an array of every progress/tool line for each returned session, even when its newest eight messages came first.
71
+ function* linesIn(text, latest) {
72
+ if (!text.length) return;
73
+ const limit = text.endsWith('\n') ? text.length - 1 : text.length;
74
+ if (latest) {
75
+ let end = limit;
76
+ for (;;) {
77
+ const newline = end === 0 ? -1 : text.lastIndexOf('\n', end - 1);
78
+ yield text.slice(newline + 1, end);
79
+ if (newline < 0) return;
80
+ end = newline;
81
+ }
82
+ } else {
83
+ let from = 0;
84
+ for (;;) {
85
+ const newline = text.indexOf('\n', from);
86
+ if (newline < 0 || newline >= limit) { yield text.slice(from, limit); return; }
87
+ yield text.slice(from, newline);
88
+ from = newline + 1;
89
+ }
90
+ }
91
+ }
92
+
69
93
  function codexCandidates(lines, latest) {
70
94
  const candidates = [];
71
95
  let pending = null;
@@ -90,9 +114,7 @@ function candidatesIn(bytes, harness, latest) {
90
114
  // Validate the entire window before selecting eight records, matching the
91
115
  // native String::from_utf8 refusal even for unused later preview bytes.
92
116
  const text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
93
- const lines = text.split('\n');
94
- if (lines.at(-1) === '') lines.pop();
95
- if (latest) lines.reverse();
117
+ const lines = linesIn(text, latest);
96
118
  if (harness === 'codex') return codexCandidates(lines, latest);
97
119
  const candidates = [];
98
120
  for (const line of lines) {