trajectories 0.0.0 → 0.1.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.
Files changed (204) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +201 -0
  3. package/README.md +274 -2
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +8542 -0
  6. package/dist/convert.d.ts +31 -0
  7. package/dist/convert.js +431 -0
  8. package/dist/index.d.ts +7 -0
  9. package/dist/index.js +8402 -0
  10. package/dist/log-baseline.d.ts +26 -0
  11. package/dist/log-baseline.js +104 -0
  12. package/dist/log-content.d.ts +16 -0
  13. package/dist/log-content.js +80 -0
  14. package/dist/log-io.d.ts +10 -0
  15. package/dist/log-io.js +121 -0
  16. package/dist/log-read.d.ts +24 -0
  17. package/dist/log-read.js +117 -0
  18. package/dist/log-reconcile.d.ts +5 -0
  19. package/dist/log-reconcile.js +38 -0
  20. package/dist/log-records.d.ts +35 -0
  21. package/dist/log-records.js +85 -0
  22. package/dist/log-recovery.d.ts +18 -0
  23. package/dist/log-recovery.js +123 -0
  24. package/dist/log-stage.d.ts +14 -0
  25. package/dist/log-stage.js +138 -0
  26. package/dist/log-state.d.ts +89 -0
  27. package/dist/log-state.js +92 -0
  28. package/dist/log-switch.d.ts +3 -0
  29. package/dist/log-switch.js +98 -0
  30. package/dist/log-types.d.ts +129 -0
  31. package/dist/log-types.js +8 -0
  32. package/dist/log.d.ts +8 -0
  33. package/dist/log.js +62 -0
  34. package/dist/migrate.d.ts +29 -0
  35. package/dist/migrate.js +173 -0
  36. package/dist/normalization-runtime.d.ts +1 -0
  37. package/dist/normalization-runtime.js +3150 -0
  38. package/dist/portable.d.ts +9 -0
  39. package/dist/portable.js +26 -0
  40. package/dist/readers/canonical.d.ts +13 -0
  41. package/dist/readers/canonical.js +54 -0
  42. package/dist/readers/cline.d.ts +10 -0
  43. package/dist/readers/cline.js +107 -0
  44. package/dist/readers/cursor.d.ts +20 -0
  45. package/dist/readers/cursor.js +374 -0
  46. package/dist/readers/deepagents.d.ts +41 -0
  47. package/dist/readers/deepagents.js +163 -0
  48. package/dist/readers/fx.d.ts +11 -0
  49. package/dist/readers/fx.js +573 -0
  50. package/dist/readers/github-copilot.d.ts +8 -0
  51. package/dist/readers/github-copilot.js +216 -0
  52. package/dist/readers/grok-build.d.ts +12 -0
  53. package/dist/readers/grok-build.js +391 -0
  54. package/dist/readers/guards.d.ts +4 -0
  55. package/dist/readers/guards.js +121 -0
  56. package/dist/readers/pi-compaction.d.ts +7 -0
  57. package/dist/readers/pi-compaction.js +112 -0
  58. package/dist/readers/pi-context.d.ts +13 -0
  59. package/dist/readers/pi-context.js +64 -0
  60. package/dist/store.d.ts +25 -0
  61. package/dist/store.js +218 -0
  62. package/dist/types.d.ts +91 -0
  63. package/dist/types.js +1 -0
  64. package/dist/writers/claude-code.d.ts +5 -0
  65. package/dist/writers/claude-code.js +51 -0
  66. package/dist/writers/cline.d.ts +3 -0
  67. package/dist/writers/cline.js +77 -0
  68. package/dist/writers/codex.d.ts +3 -0
  69. package/dist/writers/codex.js +32 -0
  70. package/dist/writers/cursor.d.ts +3 -0
  71. package/dist/writers/cursor.js +197 -0
  72. package/dist/writers/deepagents.d.ts +3 -0
  73. package/dist/writers/deepagents.js +116 -0
  74. package/dist/writers/entropy.d.ts +5 -0
  75. package/dist/writers/entropy.js +33 -0
  76. package/dist/writers/fx.d.ts +3 -0
  77. package/dist/writers/fx.js +135 -0
  78. package/dist/writers/github-copilot.d.ts +3 -0
  79. package/dist/writers/github-copilot.js +71 -0
  80. package/dist/writers/grok-build.d.ts +3 -0
  81. package/dist/writers/grok-build.js +43 -0
  82. package/dist/writers/helpers.d.ts +17 -0
  83. package/dist/writers/helpers.js +75 -0
  84. package/dist/writers/omp.d.ts +3 -0
  85. package/dist/writers/omp.js +7 -0
  86. package/dist/writers/opencode.d.ts +3 -0
  87. package/dist/writers/opencode.js +65 -0
  88. package/dist/writers/pi.d.ts +3 -0
  89. package/dist/writers/pi.js +45 -0
  90. package/docs/cline-native-format.md +43 -0
  91. package/docs/copilot-native-format.md +78 -0
  92. package/docs/cursor-native-format.md +103 -0
  93. package/docs/deepagents-native-format.md +98 -0
  94. package/docs/evidence-api.md +170 -0
  95. package/docs/fx-native-format.md +87 -0
  96. package/docs/harness-parity-bootstrap.md +78 -0
  97. package/docs/harness-support-parity.md +107 -0
  98. package/docs/inspirations.md +103 -0
  99. package/docs/ledger-migration.md +141 -0
  100. package/docs/pi-compaction-context.md +75 -0
  101. package/docs/pi-provider-context.md +76 -0
  102. package/docs/research/2026-10-06-huihua-and-oar.md +176 -0
  103. package/docs/research/ledger/README.md +96 -0
  104. package/docs/research/ledger/confer/2026-09-13-round-10/README.md +61 -0
  105. package/docs/research/ledger/confer/2026-09-13-round-2/cass.md +72 -0
  106. package/docs/research/ledger/confer/2026-09-13-round-3/README.md +70 -0
  107. package/docs/research/ledger/confer/2026-09-13-round-5/README.md +61 -0
  108. package/docs/research/ledger/confer/2026-09-13-round-6/README.md +68 -0
  109. package/docs/research/ledger/confer/2026-09-13-round-7/README.md +64 -0
  110. package/docs/research/ledger/confer/2026-09-13-round-9/README.md +59 -0
  111. package/docs/research/ledger/confer/2026-09-14-wake/wake.md +146 -0
  112. package/docs/research/ledger/docs/inspirations.md +320 -0
  113. package/docs/research/ledger/docs/pr2-pr6-integration.md +53 -0
  114. package/docs/research/ledger/docs/research/2026-09-07-confer-inspirations.md +163 -0
  115. package/docs/research/ledger/docs/research/2026-09-07-letta-continuous-service.md +104 -0
  116. package/docs/research/ledger/docs/research/2026-09-08-claude-history-retrieval.md +117 -0
  117. package/docs/research/ledger/docs/research/2026-09-09-agentsview-native-discovery.md +87 -0
  118. package/docs/research/ledger/docs/research/2026-09-09-daemon-observation.md +231 -0
  119. package/docs/research/ledger/docs/research/2026-09-09-discovery-pagination.md +114 -0
  120. package/docs/research/ledger/docs/research/2026-09-09-harness-transcript-contract.md +145 -0
  121. package/docs/research/ledger/docs/research/2026-09-09-native-history-discovery.md +180 -0
  122. package/docs/research/ledger/docs/research/2026-09-09-native-history-implementation.md +137 -0
  123. package/docs/research/ledger/docs/research/2026-09-09-other-inspirations.md +279 -0
  124. package/docs/research/ledger/docs/research/2026-09-09-trajectory-and-letta-code.md +128 -0
  125. package/docs/research/ledger/docs/research/2026-09-13-diagnostics-coverage.md +116 -0
  126. package/docs/research/ledger/docs/research/2026-09-13-fts-rebuild.md +83 -0
  127. package/docs/research/ledger/docs/research/2026-09-13-read-path-split.md +74 -0
  128. package/docs/research/ledger/docs/research/2026-09-13-runtime-rollout.md +102 -0
  129. package/docs/research/ledger/docs/research/2026-09-13-scan-outcomes.md +80 -0
  130. package/docs/research/ledger/docs/research/2026-09-13-search-quality.md +105 -0
  131. package/docs/research/ledger/docs/research/2026-09-13-source-coverage.md +64 -0
  132. package/docs/research/ledger/docs/research/2026-09-13-sync-wall-budget.md +82 -0
  133. package/docs/research/ledger/docs/research/2026-09-13-typed-read-gate.md +81 -0
  134. package/docs/research/ledger/docs/research/2026-09-13-writer-lease.md +84 -0
  135. package/docs/research/ledger/docs/research/2026-09-16-c1-copy-fallback.md +227 -0
  136. package/docs/research/ledger/docs/research/2026-09-16-c10-kimi.md +414 -0
  137. package/docs/research/ledger/docs/research/2026-09-16-c2-derive-counter.md +216 -0
  138. package/docs/research/ledger/docs/research/2026-09-16-c3-echo-filter.md +305 -0
  139. package/docs/research/ledger/docs/research/2026-09-16-c4-title-index.md +349 -0
  140. package/docs/research/ledger/docs/research/2026-09-16-c5-trigram.md +268 -0
  141. package/docs/research/ledger/docs/research/2026-09-16-c6-recency-prior.md +438 -0
  142. package/docs/research/ledger/docs/research/2026-09-16-c7-tombstones.md +467 -0
  143. package/docs/research/ledger/docs/research/2026-09-16-c8-remote-truth.md +390 -0
  144. package/docs/research/ledger/docs/research/2026-09-16-c9-grok.md +380 -0
  145. package/docs/research/ledger/docs/research/2026-09-16-wake-port-program.md +38 -0
  146. package/docs/research/ledger/docs/research/2026-09-17-cursor-blobstore.md +157 -0
  147. package/docs/research/ledger/manifest.json +540 -0
  148. package/legacy/NOTICE.md +50 -0
  149. package/legacy/bin/ledger-core.ts +7 -0
  150. package/legacy/bin/ledger.ts +7 -0
  151. package/legacy/bin/traj-bridge.ts +7 -0
  152. package/legacy/scripts/import-claude-history.ts +130 -0
  153. package/legacy/scripts/native-history-smoke.ts +52 -0
  154. package/legacy/scripts/observe-daemon.ts +570 -0
  155. package/legacy/scripts/retitle-sessions.md +19 -0
  156. package/legacy/scripts/verify-runtime-rollout.ts +151 -0
  157. package/legacy/src/canonical.ts +108 -0
  158. package/legacy/src/cli.ts +1293 -0
  159. package/legacy/src/db.ts +2254 -0
  160. package/legacy/src/diagnostics.ts +87 -0
  161. package/legacy/src/discovery.ts +56 -0
  162. package/legacy/src/embeddings.ts +382 -0
  163. package/legacy/src/filter.ts +602 -0
  164. package/legacy/src/gemini_embeddings.ts +103 -0
  165. package/legacy/src/identity.ts +9 -0
  166. package/legacy/src/index.ts +10 -0
  167. package/legacy/src/ingest.ts +791 -0
  168. package/legacy/src/managed.ts +178 -0
  169. package/legacy/src/native/common.ts +68 -0
  170. package/legacy/src/native/cursor.ts +647 -0
  171. package/legacy/src/native/grok.ts +540 -0
  172. package/legacy/src/native/kimi.ts +706 -0
  173. package/legacy/src/native/opencode.ts +470 -0
  174. package/legacy/src/native/sqlite-ro.ts +281 -0
  175. package/legacy/src/native.ts +97 -0
  176. package/legacy/src/normalizer.ts +457 -0
  177. package/legacy/src/parentage.ts +562 -0
  178. package/legacy/src/pressure.ts +162 -0
  179. package/legacy/src/private-storage.ts +74 -0
  180. package/legacy/src/reader.ts +1255 -0
  181. package/legacy/src/scanner.ts +591 -0
  182. package/legacy/src/schema-gate.ts +263 -0
  183. package/legacy/src/schema.ts +829 -0
  184. package/legacy/src/semantic-top-k.ts +71 -0
  185. package/legacy/src/session-hash.ts +49 -0
  186. package/legacy/src/store-status.ts +218 -0
  187. package/legacy/src/sync-preparation.ts +45 -0
  188. package/legacy/src/sync-truth.ts +843 -0
  189. package/legacy/src/sync.ts +1219 -0
  190. package/legacy/src/types.ts +436 -0
  191. package/legacy/src/vocab.txt +30522 -0
  192. package/legacy/src/watch.ts +673 -0
  193. package/legacy/src/writer-lease.ts +310 -0
  194. package/ops/README.md +120 -0
  195. package/ops/install-cli.sh +63 -0
  196. package/ops/install-release.sh +49 -0
  197. package/ops/linux/install-systemd-user.sh +103 -0
  198. package/ops/macos/install-launchd.sh +72 -0
  199. package/ops/sync-settings.sh +37 -0
  200. package/package.json +19 -5
  201. package/skills/trajectories/SKILL.md +138 -0
  202. package/skills/trajectories/scripts/trajectories_recent.sh +26 -0
  203. package/skills/trajectories/scripts/trajectories_search.sh +26 -0
  204. package/index.js +0 -1
@@ -0,0 +1,130 @@
1
+ import { normalizeToCanonical } from "@letta-ai/trajectory";
2
+ import { createHash } from "node:crypto";
3
+ import { createReadStream, mkdirSync, writeFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+ import { parseArgs } from "node:util";
6
+ import { TrajectoryDb } from "../src/db.js";
7
+ import { ledgerId } from "../src/identity.js";
8
+ import type { CanonicalDbRecord, SessionRecord } from "../src/types.js";
9
+ import { acquireWriterLease, isMemoryStorePath } from "../src/writer-lease.js";
10
+
11
+ type SliceOptions = { history: string; session: string; from: string; to: string };
12
+ const sha256 = (value: string | Buffer) => createHash("sha256").update(value).digest("hex");
13
+
14
+ async function* sourceLines(path: string) {
15
+ let pending = Buffer.alloc(0), offset = 0, line = 0;
16
+ for await (const chunk of createReadStream(path)) {
17
+ pending = Buffer.concat([pending, chunk as Buffer]);
18
+ let end: number;
19
+ while ((end = pending.indexOf(10)) !== -1) {
20
+ const bytes = pending.subarray(0, end + 1);
21
+ yield { bytes, offset, line: ++line };
22
+ offset += bytes.length;
23
+ pending = pending.subarray(end + 1);
24
+ }
25
+ }
26
+ if (pending.length) yield { bytes: pending, offset, line: ++line };
27
+ }
28
+
29
+ export async function prepareHistorySlice(options: SliceOptions) {
30
+ const from = Date.parse(options.from), to = Date.parse(options.to);
31
+ const zoned = /T.*(?:Z|[+-]\d{2}:\d{2})$/;
32
+ if (!zoned.test(options.from) || !zoned.test(options.to) || !Number.isFinite(from) || !Number.isFinite(to) || from >= to) {
33
+ throw new Error("from/to must be increasing ISO timestamps with explicit timezones");
34
+ }
35
+ if (!options.session) throw new Error("An explicit session ID is required");
36
+ const history = resolve(options.history);
37
+ const scope = { history, session: options.session, from: new Date(from).toISOString(), to: new Date(to).toISOString() };
38
+ const sliceId = `claude-history:${options.session}:${sha256(JSON.stringify(scope))}`;
39
+ const sessionId = ledgerId("claude-code", sliceId);
40
+ const records: CanonicalDbRecord[] = [];
41
+ const evidence: Array<{ record_id: string; line: number; byte_offset: number; byte_length: number; raw_sha256: string }> = [];
42
+ const rows: Buffer[] = [];
43
+ let scanned = 0, cwd: string | null = null;
44
+ for await (const { bytes, offset, line } of sourceLines(history)) {
45
+ scanned++;
46
+ let row;
47
+ try { row = JSON.parse(bytes.toString("utf8")); }
48
+ catch { throw new Error(`Invalid JSON in history at line ${line}`); }
49
+ if (row.sessionId !== options.session || typeof row.timestamp !== "number" || row.timestamp < from || row.timestamp >= to) continue;
50
+ if (typeof row.display !== "string" || !row.display.trim()) continue;
51
+ if (typeof row.project === "string") cwd ??= row.project;
52
+
53
+ // This is a partial prompt projection, not a recovered assistant transcript.
54
+ // Per-row normalization retains locations in the original file, not a reserialized envelope.
55
+ const result = normalizeToCanonical({
56
+ source: "claude-code",
57
+ transcript: JSON.stringify({ type: "user", timestamp: row.timestamp, message: { content: row.display } }),
58
+ sourceContext: { groupId: sliceId, baseByteOffset: offset, partial: true },
59
+ });
60
+ const users = result.records.filter((r) => r.record_type === "user");
61
+ if (users.length !== 1 || users[0].content !== row.display || users[0].source_timestamp !== new Date(row.timestamp).toISOString()) {
62
+ throw new Error(`History mapping changed text or timestamp at line ${line}`);
63
+ }
64
+ const r = users[0];
65
+ records.push({
66
+ id: ledgerId("claude-code", r.record_id), session_id: sessionId, source_type: r.source_type,
67
+ record_type: r.record_type, stable_source_record_id: r.stable_source_record_id ?? null,
68
+ source_identity_kind: r.source_identity_kind ?? null,
69
+ source_order_id: r.source_order_id, component_index: r.component_index,
70
+ record_timestamp: r.record_timestamp ?? null, source_timestamp: r.source_timestamp ?? null,
71
+ content: r.content ?? null, tool_call_id: null, tool_name: null,
72
+ tool_arguments_json: null, tool_result_json: null, tool_result_ok: null,
73
+ record_json: r.record_json, content_hash: r.content_hash, record_hash: r.record_hash,
74
+ });
75
+ rows.push(Buffer.from(bytes));
76
+ evidence.push({ record_id: ledgerId("claude-code", r.record_id), line, byte_offset: offset, byte_length: bytes.length, raw_sha256: sha256(bytes) });
77
+ }
78
+ if (!records.length) throw new Error("No prompts in the selected session/time window; nothing imported");
79
+ const raw = Buffer.concat(rows);
80
+ const timestamps = records.map((r) => r.source_timestamp!).sort();
81
+ const now = new Date().toISOString();
82
+ const session: SessionRecord = {
83
+ id: sessionId, source: "claude-code", source_path: history,
84
+ content_hash: sha256(JSON.stringify(records.map((r) => [r.id, r.record_hash]))),
85
+ title: `Claude prompt history (partial): ${scope.from} to ${scope.to}`,
86
+ cwd, model: null, started_at: timestamps[0], ended_at: timestamps.at(-1)!,
87
+ record_count: records.length, created_at: now, updated_at: now,
88
+ };
89
+ return {
90
+ session, records, raw,
91
+ provenance: { mapping: "claude-history-prompt-slice-v1", partial: true, scope, scanned_lines: scanned,
92
+ selected_prompts: records.length, fixture_sha256: sha256(raw), records: evidence },
93
+ };
94
+ }
95
+
96
+ export function saveHistorySlice(slice: Awaited<ReturnType<typeof prepareHistorySlice>>, dbPath: string, evidenceDir: string) {
97
+ const lease = isMemoryStorePath(dbPath) ? null : acquireWriterLease(dbPath, { holder: "import-claude-history" });
98
+ try {
99
+ const directory = resolve(evidenceDir);
100
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
101
+ writeFileSync(resolve(directory, "selected-history.jsonl"), slice.raw, { mode: 0o600 });
102
+ writeFileSync(resolve(directory, "provenance.json"), JSON.stringify(slice.provenance, null, 2) + "\n", { mode: 0o600 });
103
+ const db = new TrajectoryDb(dbPath);
104
+ try {
105
+ return { ...db.saveSession(slice.session, slice.records), session_id: slice.session.id,
106
+ prompts: slice.records.length, fixture_sha256: slice.provenance.fixture_sha256, evidence: directory };
107
+ } finally { db.close(); }
108
+ } finally { lease?.close(); }
109
+ }
110
+
111
+ export async function runHistoryImport(argv: string[]): Promise<void> {
112
+ const { values } = parseArgs({ args: argv, options: {
113
+ history: { type: "string" }, session: { type: "string" }, from: { type: "string" },
114
+ to: { type: "string" }, db: { type: "string" }, evidence: { type: "string" },
115
+ robot: { type: "boolean" }, json: { type: "boolean" },
116
+ } });
117
+ if (!values.history || !values.session || !values.from || !values.to || !values.db || !values.evidence) {
118
+ throw new Error("Required: --history --session --from --to --db --evidence (local private directory)");
119
+ }
120
+ const started = performance.now(), cpu = process.cpuUsage(), baseline = process.memoryUsage().rss;
121
+ const slice = await prepareHistorySlice({ history: values.history, session: values.session, from: values.from, to: values.to });
122
+ const result = saveHistorySlice(slice, values.db, values.evidence);
123
+ const used = process.cpuUsage(cpu);
124
+ console.log(JSON.stringify({ ...result, wall_seconds: (performance.now() - started) / 1000,
125
+ cpu_seconds: (used.user + used.system) / 1e6, rss_baseline_bytes: baseline,
126
+ process_max_rss_bytes: process.resourceUsage().maxRSS * 1024,
127
+ rss_before_exit_bytes: process.memoryUsage().rss, embeddings: "not generated; lexical index only" }));
128
+ }
129
+
130
+ if (import.meta.main) await runHistoryImport(process.argv.slice(2));
@@ -0,0 +1,52 @@
1
+ import { chmodSync, mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { TrajectoryDb } from "../src/db.js";
5
+ import { ingestDiscoveredSession, sessionNeedsIngest } from "../src/ingest.js";
6
+ import { scanSessions } from "../src/scanner.js";
7
+ import type { ScanOutcome } from "../src/types.js";
8
+
9
+ process.umask(0o077);
10
+ const [source, path] = process.argv.slice(2);
11
+ if (!["cursor", "opencode"].includes(source) || !path || !/\.(?:db|vscdb|sqlite|sqlite3)$/i.test(path)) {
12
+ throw new Error("Usage: bun scripts/native-history-smoke.ts <cursor|opencode> <native-database-path>");
13
+ }
14
+ const root = mkdtempSync(join(tmpdir(), "trajectories-native-smoke-"));
15
+ chmodSync(root, 0o700);
16
+ const db = new TrajectoryDb(join(root, "trajectories.sqlite"));
17
+ const report = {
18
+ source, candidateLimit: 3, byteLimit: 8 * 1024 * 1024, discovered: 0,
19
+ diagnostics: {} as Record<string, number>, outcomes: [] as ScanOutcome[],
20
+ attempted: 0, stored: 0,
21
+ records: 0, recordKinds: {} as Record<string, number>,
22
+ duplicateFree: false, unchanged: false, needsIngest: true,
23
+ };
24
+ try {
25
+ const { sessions: items, outcomes } = await scanSessions({ sources: [source], roots: { [source]: path }, limit: 3,
26
+ onDiagnostic: diagnostic => { report.diagnostics[diagnostic.code] = (report.diagnostics[diagnostic.code] ?? 0) + 1; },
27
+ });
28
+ report.outcomes = outcomes;
29
+ report.discovered = items.length;
30
+ for (const item of items) {
31
+ report.attempted++;
32
+ const result = await ingestDiscoveredSession(db, item, { embed: false, maxRecordBytes: report.byteLimit });
33
+ if (result.kind !== "imported" || !result.sessionId) continue;
34
+ const records = db.getSessionRecords(result.sessionId);
35
+ report.stored = db.getSessionCount();
36
+ if (!records.some(record => record.record_type !== "meta")) continue;
37
+ report.records = records.length;
38
+ for (const record of records) report.recordKinds[record.record_type] = (report.recordKinds[record.record_type] ?? 0) + 1;
39
+ report.duplicateFree = new Set(records.map(record => record.id)).size === records.length;
40
+ report.unchanged = (await ingestDiscoveredSession(db, item, { embed: false, maxRecordBytes: report.byteLimit })).kind === "unchanged";
41
+ report.needsIngest = sessionNeedsIngest(db, item, { maxRecordBytes: report.byteLimit });
42
+ break;
43
+ }
44
+ console.log(JSON.stringify(report));
45
+ if (!report.records || !report.duplicateFree || !report.unchanged || report.needsIngest) process.exitCode = 1;
46
+ } catch {
47
+ console.log(JSON.stringify({ ...report, error: "native smoke failed; private error details suppressed" }));
48
+ process.exitCode = 1;
49
+ } finally {
50
+ db.close();
51
+ rmSync(root, { recursive: true, force: true });
52
+ }