trajectories 0.0.0 → 0.1.2

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 (205) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +201 -0
  3. package/README.md +288 -2
  4. package/dist/cli.js +30210 -0
  5. package/dist/convert.d.ts +31 -0
  6. package/dist/convert.js +431 -0
  7. package/dist/deepagents_checkpoint.py +362 -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 +1233 -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 +150 -0
  195. package/ops/install-cli.sh +63 -0
  196. package/ops/install-native.sh +35 -0
  197. package/ops/install-release.sh +49 -0
  198. package/ops/linux/install-systemd-user.sh +103 -0
  199. package/ops/macos/install-launchd.sh +72 -0
  200. package/ops/sync-settings.sh +37 -0
  201. package/package.json +21 -5
  202. package/skills/trajectories/SKILL.md +138 -0
  203. package/skills/trajectories/scripts/trajectories_recent.sh +26 -0
  204. package/skills/trajectories/scripts/trajectories_search.sh +26 -0
  205. package/index.js +0 -1
@@ -0,0 +1,5 @@
1
+ import type { NativeWriter } from '../types.js';
2
+ /** Claude's project bucket algorithm, mirrored in its official Agent SDK. */
3
+ export declare function claudeProjectKey(cwd: string): string;
4
+ /** Claude Code's parentUuid transcript chain; resume uses an isolated config directory. */
5
+ export declare const writeClaudeCode: NativeWriter;
@@ -0,0 +1,51 @@
1
+ import { destination, droppedRecord, jsonl, parseArguments, timestampOf, toolNames, usageDiagnostic } from './helpers.js';
2
+ import { randomNativeEntropy } from './entropy.js';
3
+ /** Claude's project bucket algorithm, mirrored in its official Agent SDK. */
4
+ export function claudeProjectKey(cwd) {
5
+ const sanitized = cwd.replace(/[^a-zA-Z0-9]/g, '-');
6
+ if (sanitized.length <= 200)
7
+ return sanitized;
8
+ let hash = 0;
9
+ for (let i = 0; i < cwd.length; i++)
10
+ hash = ((hash << 5) - hash + cwd.charCodeAt(i)) | 0;
11
+ return `${sanitized.slice(0, 200)}-${Math.abs(hash).toString(36)}`;
12
+ }
13
+ /** Claude Code's parentUuid transcript chain; resume uses an isolated config directory. */
14
+ export const writeClaudeCode = (records, options, entropy = randomNativeEntropy) => {
15
+ const { id, timestamp } = destination(options, entropy);
16
+ toolNames(records);
17
+ const diagnostics = [usageDiagnostic()];
18
+ const entries = [];
19
+ let parentUuid = null;
20
+ for (const record of records) {
21
+ if (droppedRecord(record, diagnostics) || record.role === 'meta')
22
+ continue;
23
+ const at = timestampOf(record);
24
+ let type;
25
+ let message;
26
+ if (record.role === 'user') {
27
+ type = 'user';
28
+ message = { role: 'user', content: record.content };
29
+ }
30
+ else if (record.role === 'assistant') {
31
+ type = 'assistant';
32
+ message = { id: `msg_${entropy.uuid().replaceAll('-', '')}`, type: 'message', role: 'assistant', model: 'trajectories-import',
33
+ content: 'tool_calls' in record ? record.tool_calls.map((call) => ({ type: 'tool_use', id: call.id, name: call.name, input: parseArguments(call.args) })) : [{ type: 'text', text: record.content }],
34
+ stop_reason: 'tool_calls' in record ? 'tool_use' : 'end_turn', stop_sequence: null,
35
+ usage: { input_tokens: 0, output_tokens: 0 } };
36
+ }
37
+ else if (record.role === 'tool') {
38
+ type = 'user';
39
+ message = { role: 'user', content: [{ type: 'tool_result', tool_use_id: record.tool_call_id, content: record.content, ...(record.ok === undefined ? {} : { is_error: !record.ok }) }] };
40
+ }
41
+ else
42
+ continue;
43
+ const uuid = entropy.uuid();
44
+ entries.push({ type, uuid, parentUuid, sessionId: id, cwd: options.cwd, timestamp: at || timestamp,
45
+ isSidechain: false, userType: 'external', version: '2.1.285', message });
46
+ parentUuid = uuid;
47
+ }
48
+ const entrypoint = `projects/${claudeProjectKey(options.cwd)}/${id}.jsonl`;
49
+ return { harness: 'claude-code', sessionId: id, files: [{ path: entrypoint, content: jsonl(entries) }], entrypoint,
50
+ resume: { cwd: options.cwd, command: 'env', args: [`CLAUDE_CONFIG_DIR=${options.directory}`, 'claude', '--resume', id, ...(options.model ? ['--model', options.model] : [])] }, diagnostics };
51
+ };
@@ -0,0 +1,3 @@
1
+ import type { NativeWriter } from '../types.js';
2
+ /** Native Cline Core persisted messages plus the AgentRuntime history used by HarnessAgent. */
3
+ export declare const writeCline: NativeWriter;
@@ -0,0 +1,77 @@
1
+ import { resolve } from 'node:path';
2
+ import { destination, droppedRecord, parseArguments, timestampOf, toolNames } from './helpers.js';
3
+ import { randomNativeEntropy } from './entropy.js';
4
+ /** Native Cline Core persisted messages plus the AgentRuntime history used by HarnessAgent. */
5
+ export const writeCline = (records, options, entropy = randomNativeEntropy) => {
6
+ const { id, timestamp } = destination(options, entropy);
7
+ const names = toolNames(records);
8
+ const diagnostics = [];
9
+ if (!options.provider)
10
+ diagnostics.push({ code: 'unknown-provider', message: 'No target provider was supplied; the manifest records unknown. Select the provider when resuming.' });
11
+ const messages = [];
12
+ const history = [];
13
+ let previous = '';
14
+ for (const record of records) {
15
+ if (droppedRecord(record, diagnostics))
16
+ continue;
17
+ if (record.role === 'meta')
18
+ continue;
19
+ const at = Date.parse(timestampOf(record));
20
+ const calls = record.role === 'assistant' && 'tool_calls' in record;
21
+ const kind = calls ? 'calls' : record.role === 'tool' ? 'results' : 'text';
22
+ const nativeRole = record.role === 'tool' ? 'user' : record.role;
23
+ let message = messages.at(-1), agentMessage = history.at(-1);
24
+ if (kind === 'text' || previous !== kind) {
25
+ const messageId = entropy.uuid();
26
+ message = { id: messageId, role: nativeRole, content: [], ts: at, agent: 'lead', sessionId: id };
27
+ agentMessage = { id: messageId, role: record.role, content: [], createdAt: at };
28
+ messages.push(message);
29
+ history.push(agentMessage);
30
+ }
31
+ if (calls) {
32
+ for (const call of record.tool_calls) {
33
+ const input = parseArguments(call.args);
34
+ message.content.push({ type: 'tool_use', id: call.id, name: call.name, input });
35
+ agentMessage.content.push({ type: 'tool-call', toolCallId: call.id, toolName: call.name, input });
36
+ }
37
+ }
38
+ else if (record.role === 'tool') {
39
+ message.content.push({ type: 'tool_result', tool_use_id: record.tool_call_id, name: names.get(record.tool_call_id), content: record.content, ...(record.ok !== undefined ? { is_error: !record.ok } : {}) });
40
+ agentMessage.content.push({ type: 'tool-result', toolCallId: record.tool_call_id, toolName: names.get(record.tool_call_id), output: record.content, ...(record.ok !== undefined ? { isError: !record.ok } : {}) });
41
+ }
42
+ else {
43
+ message.content.push({ type: 'text', text: record.content });
44
+ agentMessage.content.push({ type: 'text', text: record.content });
45
+ }
46
+ previous = kind;
47
+ }
48
+ const root = '.cline/data/sessions';
49
+ const entrypoint = `${root}/${id}/${id}.messages.json`;
50
+ const manifest = { version: 1, session_id: id, source: 'cli', pid: 0, started_at: timestamp, ended_at: timestamp, exit_code: 0, status: 'completed', interactive: true,
51
+ provider: options.provider ?? 'unknown', model: options.model ?? 'unknown', cwd: options.cwd, workspace_root: options.cwd,
52
+ enable_tools: true, enable_spawn: false, enable_teams: false, messages_path: resolve(options.directory, entrypoint), metadata: { title: 'Imported conversation' } };
53
+ const origin = { source: 'cli', mode: 'user', sessionId: id };
54
+ const payload = { version: 1, updated_at: timestamp, agent: 'lead', sessionId: id, origin, messages };
55
+ const importer = `import { readFile } from 'node:fs/promises';
56
+ import { CoreSessionService, SqliteSessionStore } from '@cline/core';
57
+ const [directory, id] = process.argv.slice(2);
58
+ const path = directory + '/' + id + '/' + id;
59
+ const payload = JSON.parse(await readFile(path + '.messages.json', 'utf8'));
60
+ const manifest = JSON.parse(await readFile(path + '.json', 'utf8'));
61
+ const store = new SqliteSessionStore({ sessionsDir: directory });
62
+ store.init();
63
+ try {
64
+ if (store.get(id)) throw new Error('Cline destination session is already indexed.');
65
+ const service = new CoreSessionService(store, { sessionArtifactsDir: directory });
66
+ await service.createRootSessionWithArtifacts({ sessionId: id, source: 'cli', pid: 0, startedAt: manifest.started_at, endedAt: manifest.ended_at, exitCode: 0, status: 'completed', interactive: true,
67
+ provider: manifest.provider, model: manifest.model, cwd: manifest.cwd, workspaceRoot: manifest.workspace_root, enableTools: true, enableSpawn: false, enableTeams: false, metadata: manifest.metadata });
68
+ await service.persistSessionMessages(id, payload.messages);
69
+ } finally { store.close(); }
70
+ `;
71
+ return { harness: 'cline', sessionId: id, entrypoint,
72
+ files: [{ path: entrypoint, content: JSON.stringify(payload, null, 2) + '\n' }, { path: `${root}/${id}/${id}.json`, content: JSON.stringify(manifest, null, 2) + '\n' },
73
+ { path: 'history.json', content: JSON.stringify(history, null, 2) + '\n' }, { path: 'prepare-cline.mjs', content: importer }],
74
+ prepare: [{ command: 'node', args: [resolve(options.directory, 'prepare-cline.mjs'), resolve(options.directory, root), id], cwd: options.cwd }],
75
+ resume: { command: 'env', args: [`HOME=${options.directory}`, `CLINE_SESSION_DATA_DIR=${resolve(options.directory, root)}`, 'cline', '--data-dir', resolve(options.directory, '.cline/data'), '--id', id,
76
+ ...(options.provider ? ['--provider', options.provider] : []), ...(options.model ? ['--model', options.model] : [])], cwd: options.cwd }, diagnostics };
77
+ };
@@ -0,0 +1,3 @@
1
+ import type { NativeWriter } from '../types.js';
2
+ /** Codex rollout JSONL, consumed by the native rollout loader on resume. */
3
+ export declare const writeCodex: NativeWriter;
@@ -0,0 +1,32 @@
1
+ import { destination, droppedRecord, jsonl, timestampOf, toolNames } from './helpers.js';
2
+ /** Codex rollout JSONL, consumed by the native rollout loader on resume. */
3
+ export const writeCodex = (records, options, entropy) => {
4
+ const { id, timestamp } = destination(options, entropy);
5
+ toolNames(records);
6
+ const diagnostics = [{ code: 'codex-rollout-format', message: 'Codex rollout import targets the tested native version. Internal persisted formats are not a stable public interchange API.' }];
7
+ const entries = [{ timestamp, type: 'session_meta', payload: {
8
+ id, session_id: id, timestamp, cwd: options.cwd, originator: 'trajectories', cli_version: 'imported', source: 'cli',
9
+ model_provider: options.provider ?? null,
10
+ } }];
11
+ for (const record of records) {
12
+ if (droppedRecord(record, diagnostics) || record.role === 'meta')
13
+ continue;
14
+ const at = timestampOf(record);
15
+ const append = (payload) => entries.push({ timestamp: at, type: 'response_item', payload });
16
+ if (record.role === 'user' || (record.role === 'assistant' && !('tool_calls' in record))) {
17
+ append({ type: 'message', role: record.role, content: [{ type: record.role === 'user' ? 'input_text' : 'output_text', text: record.content }] });
18
+ }
19
+ else if (record.role === 'assistant' && 'tool_calls' in record) {
20
+ for (const call of record.tool_calls)
21
+ append({ type: 'function_call', name: call.name, call_id: call.id, arguments: call.args });
22
+ }
23
+ else if (record.role === 'tool') {
24
+ append({ type: 'function_call_output', call_id: record.tool_call_id, output: record.content });
25
+ if (record.ok !== undefined && !diagnostics.some((d) => d.code === 'untranslated-tool-status'))
26
+ diagnostics.push({ code: 'untranslated-tool-status', message: 'Codex function_call_output has no portable success flag; source status remains in the central snapshot.' });
27
+ }
28
+ }
29
+ const entrypoint = `sessions/${timestamp.slice(0, 10).replaceAll('-', '/')}/rollout-${timestamp.replaceAll(':', '-')}-${id}.jsonl`;
30
+ return { harness: 'codex', sessionId: id, files: [{ path: entrypoint, content: jsonl(entries) }], entrypoint,
31
+ resume: { cwd: options.cwd, command: 'env', args: [`CODEX_HOME=${options.directory}`, 'codex', 'resume', id, ...(options.model ? ['--model', options.model] : [])] }, diagnostics };
32
+ };
@@ -0,0 +1,3 @@
1
+ import type { NativeWriter } from '../types.js';
2
+ /** Cursor AgentKv import for the exact runtime whose serializer and loader were verified. */
3
+ export declare const writeCursor: NativeWriter;
@@ -0,0 +1,197 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { join } from 'node:path';
3
+ import { destination, parseArguments, timestampOf, toolNames } from './helpers.js';
4
+ import { randomNativeEntropy } from './entropy.js';
5
+ const TESTED_RUNTIME = '2026.10.01-e373342';
6
+ const FORMAT = 'cursor-agent-sqlite-readout-v1';
7
+ function concat(parts) { return Buffer.concat(parts.map(part => Buffer.from(part))); }
8
+ function varint(input) {
9
+ if (input < 0n)
10
+ throw new Error('Cursor protobuf integers must be non-negative.');
11
+ const bytes = [];
12
+ do {
13
+ const byte = Number(input & 0x7fn);
14
+ input >>= 7n;
15
+ bytes.push(byte | (input ? 0x80 : 0));
16
+ } while (input);
17
+ return Buffer.from(bytes);
18
+ }
19
+ function tag(number, wire) { return varint(BigInt(number * 8 + wire)); }
20
+ function bytes(number, value) { return concat([tag(number, 2), varint(BigInt(value.length)), value]); }
21
+ function text(number, value) { return bytes(number, Buffer.from(value, 'utf8')); }
22
+ function flag(number, value) { return concat([tag(number, 0), varint(value ? 1n : 0n)]); }
23
+ function digest(value) { return createHash('sha256').update(value).digest('hex'); }
24
+ function jsonValue(value) {
25
+ if (value === null)
26
+ return concat([tag(1, 0), varint(0n)]);
27
+ if (typeof value === 'number') {
28
+ if (!Number.isFinite(value))
29
+ throw new Error('Cursor tool arguments cannot contain non-finite numbers.');
30
+ const encoded = Buffer.alloc(8);
31
+ encoded.writeDoubleLE(value);
32
+ return concat([tag(2, 1), encoded]);
33
+ }
34
+ if (typeof value === 'string')
35
+ return text(3, value);
36
+ if (typeof value === 'boolean')
37
+ return flag(4, value);
38
+ if (Array.isArray(value))
39
+ return bytes(6, concat(value.map(item => bytes(1, jsonValue(item)))));
40
+ if (typeof value === 'object') {
41
+ const entries = Object.entries(value)
42
+ .map(([key, item]) => bytes(1, concat([text(1, key), bytes(2, jsonValue(item))])));
43
+ return bytes(5, concat(entries));
44
+ }
45
+ throw new Error(`Cursor tool arguments cannot encode ${typeof value}.`);
46
+ }
47
+ function blob(value, blobs) {
48
+ const id = digest(value);
49
+ blobs.set(id, Buffer.from(value));
50
+ return Buffer.from(id, 'hex');
51
+ }
52
+ function encodeTool(step) {
53
+ if (step.result === undefined || step.ok === undefined)
54
+ throw new Error(`Cursor tool call ${step.id} has no terminal result.`);
55
+ const args = concat([text(1, step.name), ...Object.entries(step.args).map(([key, value]) => bytes(2, concat([text(1, key), bytes(2, jsonValue(value))]))), text(3, step.id), text(5, step.name)]);
56
+ const content = bytes(1, text(1, step.result));
57
+ const success = concat([bytes(1, content), ...(step.ok ? [] : [flag(2, true)])]);
58
+ const mcp = concat([bytes(1, args), bytes(2, bytes(1, success))]);
59
+ return concat([bytes(15, mcp), text(57, step.id)]);
60
+ }
61
+ function nativeReadout(records, id, cwd, timestamp, model, entropy) {
62
+ const turns = [];
63
+ const calls = new Map();
64
+ let current;
65
+ for (const record of records) {
66
+ if (record.role === 'meta' || record.role === 'system' || record.role === 'observation' || record.role === 'reasoning')
67
+ continue;
68
+ if (record.role === 'user') {
69
+ timestampOf(record);
70
+ current = { user: record.content, steps: [] };
71
+ turns.push(current);
72
+ continue;
73
+ }
74
+ if (!current)
75
+ throw new Error(`Cursor native history cannot place ${record.role} before the first user turn.`);
76
+ timestampOf(record);
77
+ if (record.role === 'assistant' && !('tool_calls' in record))
78
+ current.steps.push({ kind: 'assistant', text: record.content });
79
+ else if (record.role === 'assistant') {
80
+ for (const call of record.tool_calls) {
81
+ const step = { kind: 'tool', id: call.id, name: call.name, args: parseArguments(call.args) };
82
+ current.steps.push(step);
83
+ calls.set(call.id, step);
84
+ }
85
+ }
86
+ else if (record.role === 'tool') {
87
+ const step = calls.get(record.tool_call_id);
88
+ if (!step)
89
+ throw new Error(`Cursor native history has an orphan tool result: ${record.tool_call_id}`);
90
+ if (record.ok === undefined)
91
+ throw new Error(`Cursor native history cannot infer tool status: ${record.tool_call_id}`);
92
+ step.result = record.content;
93
+ step.ok = record.ok;
94
+ }
95
+ }
96
+ if (!turns.length)
97
+ throw new Error('Cursor native history requires at least one user turn.');
98
+ if (turns.some(turn => turn.steps.length === 0))
99
+ throw new Error('Cursor native history contains an unanswered user turn.');
100
+ const blobs = new Map();
101
+ const turnRefs = [];
102
+ for (const turn of turns) {
103
+ const userRef = blob(text(1, turn.user), blobs);
104
+ const stepRefs = turn.steps.map(step => {
105
+ const payload = step.kind === 'assistant' ? bytes(1, text(1, step.text))
106
+ : step.kind === 'thinking' ? bytes(3, text(1, step.text)) : bytes(2, encodeTool(step));
107
+ return blob(payload, blobs);
108
+ });
109
+ const agent = concat([bytes(1, userRef), ...stepRefs.map(ref => bytes(2, ref))]);
110
+ turnRefs.push(blob(bytes(1, agent), blobs));
111
+ }
112
+ const root = concat(turnRefs.map(ref => bytes(8, ref)));
113
+ const rootBlobId = digest(root);
114
+ blobs.set(rootBlobId, root);
115
+ const metadata = {
116
+ agentId: id, latestRootBlobId: rootBlobId, name: 'Imported trajectory', mode: 'default', isRunEverything: false,
117
+ createdAt: Date.parse(timestamp), blobEncryptionKey: Buffer.from(entropy.bytes(32)).toString('hex'), ...(model ? { lastUsedModel: model } : {}),
118
+ };
119
+ const metadataHex = Buffer.from(JSON.stringify(metadata), 'utf8').toString('hex');
120
+ return JSON.stringify({ format: FORMAT,
121
+ source: { runtime: 'cursor-agent', version: TESTED_RUNTIME, schema: 1, store: 'store.db' },
122
+ sessionId: id, metadataHex, metaRows: [{ key: '0', value: metadataHex }], rootBlobId,
123
+ blobs: [...blobs].sort(([a], [b]) => a.localeCompare(b)).map(([blobId, data]) => ({ id: blobId, dataBase64: data.toString('base64') })),
124
+ provenance: { cwd, createdAt: timestamp, sourceRecords: records },
125
+ }, null, 2) + '\n';
126
+ }
127
+ const PREPARE = String.raw `import { createHash } from 'node:crypto';
128
+ import { copyFile, lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
129
+ import { dirname, join, resolve } from 'node:path';
130
+ import { spawnSync } from 'node:child_process';
131
+ import { DatabaseSync } from 'node:sqlite';
132
+
133
+ const artifact = resolve(process.argv[2] ?? '');
134
+ const output = resolve(process.argv[3] ?? '');
135
+ if (!artifact || !output) throw new Error('usage: node prepare-cursor.mjs <readout.json> <output-directory>');
136
+ const runtime = process.env.CURSOR_AGENT_BIN || 'cursor-agent';
137
+ const version = spawnSync(runtime, ['--version'], { encoding: 'utf8' });
138
+ if (version.status !== 0) throw new Error('cursor-agent is unavailable; set CURSOR_AGENT_BIN to the tested executable');
139
+ if (String(version.stdout).trim() !== '2026.10.01-e373342' && process.env.CURSOR_NATIVE_ALLOW_UNTESTED !== '1') {
140
+ throw new Error('Cursor native import was verified only with cursor-agent 2026.10.01-e373342');
141
+ }
142
+ const readout = JSON.parse(await readFile(artifact, 'utf8'));
143
+ if (readout.format !== 'cursor-agent-sqlite-readout-v1' || readout.source?.schema !== 1 || !readout.provenance?.cwd) {
144
+ throw new Error('unsupported Cursor readout');
145
+ }
146
+ for (const row of readout.blobs) {
147
+ if (createHash('sha256').update(Buffer.from(row.dataBase64, 'base64')).digest('hex') !== row.id) throw new Error('corrupt Cursor blob ' + row.id);
148
+ }
149
+ const project = createHash('md5').update(resolve(readout.provenance.cwd)).digest('hex');
150
+ const cliDir = join(output, 'home', '.cursor', 'chats', project, readout.sessionId);
151
+ const acpDir = join(output, 'home', '.cursor', 'acp-sessions', readout.sessionId);
152
+ const temp = cliDir + '.tmp-' + process.pid;
153
+ if (await lstat(cliDir).then(() => true, () => false) || await lstat(acpDir).then(() => true, () => false)) {
154
+ throw new Error('refusing to replace an existing Cursor session: ' + readout.sessionId);
155
+ }
156
+ await mkdir(dirname(cliDir), { recursive: true });
157
+ await mkdir(dirname(acpDir), { recursive: true });
158
+ await rm(temp, { recursive: true, force: true });
159
+ await mkdir(temp);
160
+ const db = new DatabaseSync(join(temp, 'store.db'));
161
+ try {
162
+ db.exec('PRAGMA journal_mode=WAL; PRAGMA user_version=1; CREATE TABLE blobs (id TEXT PRIMARY KEY, data BLOB); CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT); BEGIN IMMEDIATE');
163
+ const insertBlob = db.prepare('INSERT INTO blobs (id, data) VALUES (?, ?)');
164
+ for (const row of readout.blobs) insertBlob.run(row.id, Buffer.from(row.dataBase64, 'base64'));
165
+ const insertMeta = db.prepare('INSERT INTO meta (key, value) VALUES (?, ?)');
166
+ for (const row of readout.metaRows) insertMeta.run(row.key, row.value);
167
+ db.exec('COMMIT; PRAGMA wal_checkpoint(TRUNCATE)');
168
+ } catch (error) { try { db.exec('ROLLBACK') } catch {} throw error } finally { db.close() }
169
+ try { await rename(temp, cliDir) } catch (error) { await rm(temp, { recursive: true, force: true }); throw new Error('refusing to replace an existing Cursor session: ' + cliDir, { cause: error }) }
170
+ try {
171
+ await mkdir(acpDir);
172
+ await copyFile(join(cliDir, 'store.db'), join(acpDir, 'store.db'));
173
+ await writeFile(join(acpDir, 'meta.json'), JSON.stringify({ schemaVersion: 1, cwd: resolve(readout.provenance.cwd) }) + '\n');
174
+ } catch (error) { await rm(cliDir, { recursive: true, force: true }); await rm(acpDir, { recursive: true, force: true }); throw error }
175
+ console.log(readout.sessionId);
176
+ `;
177
+ /** Cursor AgentKv import for the exact runtime whose serializer and loader were verified. */
178
+ export const writeCursor = (records, options, entropy = randomNativeEntropy) => {
179
+ if (options.provider)
180
+ throw new Error(`Cursor CLI has no provider-routing option; cannot honor provider ${options.provider}.`);
181
+ const { id, timestamp } = destination(options, entropy);
182
+ toolNames(records);
183
+ const diagnostics = [{ code: 'cursor-version-pinned',
184
+ message: `Cursor AgentKv import is pinned to cursor-agent ${TESTED_RUNTIME}; prepare refuses other versions unless the caller explicitly sets CURSOR_NATIVE_ALLOW_UNTESTED=1.` }];
185
+ for (const role of ['system', 'observation', 'reasoning'])
186
+ if (records.some(record => record.role === role))
187
+ diagnostics.push({
188
+ code: `untranslated-${role}`, message: `Source ${role} records remain verbatim in the import readout; they are not installed as target instructions or provider reasoning.`
189
+ });
190
+ const entrypoint = `cursor/${id}.readout.json`;
191
+ const helper = 'cursor/prepare-cursor.mjs';
192
+ const content = nativeReadout(records, id, options.cwd, timestamp, options.model, entropy);
193
+ return { harness: 'cursor', sessionId: id, files: [{ path: entrypoint, content }, { path: helper, content: PREPARE }], entrypoint,
194
+ prepare: [{ command: 'node', args: [join(options.directory, helper), join(options.directory, entrypoint), options.directory], cwd: options.cwd }],
195
+ resume: { command: 'env', args: [`HOME=${join(options.directory, 'home')}`, 'cursor-agent', '--resume', id,
196
+ ...(options.model ? ['--model', options.model] : [])], cwd: options.cwd }, diagnostics };
197
+ };
@@ -0,0 +1,3 @@
1
+ import type { NativeWriter } from '../types.js';
2
+ /** A portable seed plus an explicit importer using Deep Agents' official checkpoint APIs. */
3
+ export declare const writeDeepAgents: NativeWriter;
@@ -0,0 +1,116 @@
1
+ import { resolve } from 'node:path';
2
+ import { assertDeepAgentsPortable, normalizeDeepAgentsSeed } from '../readers/deepagents.js';
3
+ import { destination, droppedRecord, parseArguments, timestampOf, toolNames } from './helpers.js';
4
+ const importer = `#!/usr/bin/env python3
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ def fail(message):
12
+ raise SystemExit(message)
13
+
14
+ async def main():
15
+ if len(sys.argv) != 3:
16
+ fail("usage: import_deepagents.py CHECKPOINT_JSON SESSIONS_DB")
17
+ seed_path = Path(sys.argv[1]).resolve()
18
+ database = Path(sys.argv[2]).resolve()
19
+ if database.name != "sessions.db" or database.parent.name != ".state":
20
+ fail("destination must end in .state/sessions.db")
21
+ os.environ["DEEPAGENTS_HOME"] = str(database.parent.parent)
22
+ from deepagents_code.sessions import get_checkpointer, save_thread_seed
23
+ from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
24
+ seed = json.loads(seed_path.read_text(encoding="utf-8"))
25
+ if seed.get("version") != 1 or seed.get("source") != "deepagents":
26
+ fail("invalid Deep Agents seed")
27
+ session = seed["session"]
28
+ messages = []
29
+ message_ids = set()
30
+ for item in seed["messages"]:
31
+ if not item.get("id"):
32
+ fail("Deep Agents seed message is missing its native id")
33
+ if item["id"] in message_ids:
34
+ fail("Deep Agents seed contains a duplicate native message id")
35
+ message_ids.add(item["id"])
36
+ metadata = {"timestamp": item["timestamp"]}
37
+ if item["role"] == "human":
38
+ messages.append(HumanMessage(id=item["id"], content=item["content"], additional_kwargs=metadata))
39
+ elif item["role"] == "ai":
40
+ response = {**metadata}
41
+ if item.get("model"):
42
+ response["model_name"] = item["model"]
43
+ messages.append(AIMessage(id=item["id"], content=item["content"], tool_calls=[
44
+ {"id": call["id"], "name": call["name"], "args": call["args"], "type": "tool_call"}
45
+ for call in item["toolCalls"]
46
+ ], response_metadata=response))
47
+ elif item["role"] == "tool":
48
+ messages.append(ToolMessage(id=item["id"], content=item["content"], tool_call_id=item["toolCallId"], additional_kwargs=metadata))
49
+ else:
50
+ fail("unsupported Deep Agents message role")
51
+ values = {"messages": messages}
52
+ if session.get("model"):
53
+ values["model"] = session["model"]
54
+ config = {"configurable": {"thread_id": session["threadId"], "checkpoint_ns": ""}}
55
+ async with get_checkpointer() as checkpointer:
56
+ if await checkpointer.aget_tuple(config) is not None:
57
+ fail("destination already contains the requested Deep Agents thread")
58
+ await save_thread_seed(session["threadId"], values, agent_name="default", cwd=session["cwd"])
59
+ async with get_checkpointer() as checkpointer:
60
+ stored = await checkpointer.aget_tuple(config)
61
+ if stored is None:
62
+ fail("Deep Agents did not persist the seeded checkpoint")
63
+ metadata = dict(stored.metadata or {})
64
+ metadata.update({"acp_session": True, "cwd": session["cwd"]})
65
+ await checkpointer.aput(
66
+ config,
67
+ stored.checkpoint,
68
+ metadata,
69
+ stored.checkpoint["channel_versions"],
70
+ )
71
+
72
+ asyncio.run(main())
73
+ `;
74
+ /** A portable seed plus an explicit importer using Deep Agents' official checkpoint APIs. */
75
+ export const writeDeepAgents = (records, options, entropy) => {
76
+ const { id, timestamp } = destination(options, entropy);
77
+ toolNames(records);
78
+ const diagnostics = [];
79
+ const messages = [];
80
+ const nextMessageId = () => `trajectory:${id}:${messages.length + 1}`;
81
+ for (const record of records) {
82
+ if (droppedRecord(record, diagnostics) || record.role === 'meta')
83
+ continue;
84
+ if (record.role === 'user')
85
+ messages.push({ id: nextMessageId(), role: 'human', content: record.content, timestamp: timestampOf(record) });
86
+ else if (record.role === 'assistant') {
87
+ messages.push('tool_calls' in record
88
+ ? { id: nextMessageId(), role: 'ai', content: '', reasoning: [], toolCalls: record.tool_calls.map(call => ({
89
+ id: call.id, name: call.name, args: parseArguments(call.args),
90
+ })), ...(options.model ? { model: options.model } : {}), timestamp: timestampOf(record) }
91
+ : { id: nextMessageId(), role: 'ai', content: record.content, reasoning: [], toolCalls: [],
92
+ ...(options.model ? { model: options.model } : {}), timestamp: timestampOf(record) });
93
+ }
94
+ else if (record.role === 'tool') {
95
+ messages.push({ id: nextMessageId(), role: 'tool', content: record.content, toolCallId: record.tool_call_id, timestamp: timestampOf(record) });
96
+ if (record.ok !== undefined && !diagnostics.some(item => item.code === 'untranslated-tool-status')) {
97
+ diagnostics.push({ code: 'untranslated-tool-status', message: 'Deep Agents ToolMessage does not carry source success status. The central snapshot retains it.' });
98
+ }
99
+ }
100
+ }
101
+ const session = { threadId: id, checkpointNamespace: '', cwd: options.cwd, createdAt: timestamp,
102
+ ...(options.model ? { model: options.model } : {}) };
103
+ const seed = { version: 1, source: 'deepagents', session, messages };
104
+ assertDeepAgentsPortable(JSON.stringify(seed));
105
+ normalizeDeepAgentsSeed(seed);
106
+ const entrypoint = 'checkpoint.json';
107
+ const database = resolve(options.directory, '.state', 'sessions.db');
108
+ return { harness: 'deepagents', sessionId: id, entrypoint,
109
+ files: [{ path: entrypoint, content: JSON.stringify(seed, null, 2) + '\n' },
110
+ { path: 'import_deepagents.py', content: importer }],
111
+ prepare: [{ command: 'python3', args: [resolve(options.directory, 'import_deepagents.py'),
112
+ resolve(options.directory, entrypoint), database], cwd: options.cwd }],
113
+ resume: { command: 'env', args: [`DEEPAGENTS_HOME=${options.directory}`, 'dcode', '-r', id,
114
+ ...(options.model ? ['--model', options.provider ? `${options.provider}:${options.model}` : options.model] : [])], cwd: options.cwd },
115
+ diagnostics: [...diagnostics, { code: 'native-prepare-required', message: 'Run the explicit importer with the Python environment that contains deepagents-code before resuming.' }] };
116
+ };
@@ -0,0 +1,5 @@
1
+ import type { NativeEntropy } from '../types.js';
2
+ export declare const NATIVE_ENTROPY_VERSION = "trajectories-native-entropy-v1";
3
+ export declare const randomNativeEntropy: NativeEntropy;
4
+ /** A frozen 32-byte seed reproduces native identities without storing their bodies. */
5
+ export declare function createSeededNativeEntropy(seed: string): NativeEntropy;
@@ -0,0 +1,33 @@
1
+ import { createHmac, randomBytes, randomUUID } from 'node:crypto';
2
+ export const NATIVE_ENTROPY_VERSION = 'trajectories-native-entropy-v1';
3
+ export const randomNativeEntropy = Object.freeze({
4
+ uuid: () => randomUUID(),
5
+ bytes: (length) => randomBytes(length),
6
+ });
7
+ /** A frozen 32-byte seed reproduces native identities without storing their bodies. */
8
+ export function createSeededNativeEntropy(seed) {
9
+ if (!/^[0-9a-f]{64}$/i.test(seed))
10
+ throw new Error('Native entropy seed must contain 32 hexadecimal bytes.');
11
+ const key = Buffer.from(seed, 'hex');
12
+ let counter = 0n;
13
+ const bytes = (length) => {
14
+ if (!Number.isSafeInteger(length) || length < 0)
15
+ throw new Error('Invalid native entropy byte length.');
16
+ const output = Buffer.alloc(length);
17
+ for (let offset = 0; offset < length; offset += 32) {
18
+ const block = createHmac('sha256', key).update(`${NATIVE_ENTROPY_VERSION}\0${counter++}`).digest();
19
+ block.copy(output, offset, 0, Math.min(32, length - offset));
20
+ }
21
+ return output;
22
+ };
23
+ return {
24
+ bytes,
25
+ uuid: () => {
26
+ const value = bytes(16);
27
+ value[6] = (value[6] & 0x0f) | 0x40;
28
+ value[8] = (value[8] & 0x3f) | 0x80;
29
+ const hex = value.toString('hex');
30
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,3 @@
1
+ import type { NativeWriter } from '../types.js';
2
+ /** FX legacy session.json schema 1 with presentation execution-memory schema 3. */
3
+ export declare const writeFx: NativeWriter;