@volter/supercode-interchange 0.1.22 → 0.1.24

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 (129) hide show
  1. package/LICENSE-diffy-MIT +23 -0
  2. package/README.md +422 -2
  3. package/channel-records.mjs +71 -0
  4. package/channel-session-counts.mjs +81 -0
  5. package/channels-read.mjs +90 -0
  6. package/corpus-audit-records.mjs +192 -0
  7. package/corpus-audit-render.mjs +47 -0
  8. package/corpus-audit-schema.mjs +89 -0
  9. package/corpus-audit.d.ts +16 -0
  10. package/corpus-audit.mjs +132 -0
  11. package/file-io.mjs +21 -2
  12. package/harness-settings.mjs +19 -0
  13. package/hermes-fire-links.mjs +135 -0
  14. package/hermes-fires.mjs +106 -0
  15. package/hermes-read-home.d.ts +3 -0
  16. package/hermes-read-home.mjs +235 -0
  17. package/hermes-read-store.mjs +49 -0
  18. package/hermes-read-validation.mjs +170 -0
  19. package/hermes-read-workflow.mjs +164 -0
  20. package/hermes-read-yaml.mjs +68 -0
  21. package/hermes-store.mjs +1 -1
  22. package/homes.mjs +2 -2
  23. package/index.d.ts +14 -1
  24. package/index.mjs +1 -1
  25. package/inspection-builtin-tools.json +261 -0
  26. package/inspection-config-fields.json +765 -0
  27. package/inspection-schemas.d.ts +4 -0
  28. package/inspection-schemas.mjs +117 -0
  29. package/job-notepad-read.mjs +97 -0
  30. package/jobs-control.mjs +250 -0
  31. package/jobs-read.mjs +198 -0
  32. package/jsonl.mjs +4 -3
  33. package/memory-show.mjs +187 -0
  34. package/metadata.mjs +45 -3
  35. package/native-json.mjs +46 -3
  36. package/ontology-contracts.d.ts +5 -0
  37. package/ontology-contracts.mjs +29 -0
  38. package/ontology-operations.json +198 -0
  39. package/ontology-schemas.json +7946 -0
  40. package/openclaw-jobs-read.mjs +117 -0
  41. package/openclaw-profile-home.mjs +142 -0
  42. package/orchestrator-read-records.mjs +112 -0
  43. package/orchestrator-store.mjs +21 -9
  44. package/package.json +59 -4
  45. package/parity-ledger.json +6984 -0
  46. package/parity-orchestration.json +5538 -0
  47. package/parity-report.d.ts +7 -0
  48. package/parity-report.mjs +46 -0
  49. package/preset-profiles.mjs +27 -0
  50. package/profile-command.mjs +32 -0
  51. package/profile-orchestrator-door.mjs +104 -0
  52. package/profile-paths.mjs +25 -0
  53. package/profiles-control.mjs +98 -0
  54. package/profiles-read.mjs +119 -0
  55. package/reduction-attribution.mjs +54 -0
  56. package/reduction-diff.mjs +165 -0
  57. package/reduction-log.mjs +177 -0
  58. package/reduction-view.mjs +145 -0
  59. package/retained-projection.mjs +9 -0
  60. package/routes-read.mjs +103 -0
  61. package/runs-read.mjs +224 -0
  62. package/session-activity.d.ts +23 -0
  63. package/session-activity.mjs +124 -0
  64. package/session-append-scan.mjs +27 -17
  65. package/session-branch-handoff.mjs +115 -0
  66. package/session-claude-graph.mjs +29 -7
  67. package/session-claude-index.mjs +732 -114
  68. package/session-claude-runtime.d.ts +28 -0
  69. package/session-claude-runtime.mjs +171 -6
  70. package/session-claude-scan-store.mjs +164 -0
  71. package/session-claude.mjs +9 -0
  72. package/session-codex-activity.d.ts +6 -0
  73. package/session-codex-activity.mjs +120 -0
  74. package/session-codex-window.mjs +24 -4
  75. package/session-config-scan.mjs +141 -0
  76. package/session-convert.d.ts +33 -0
  77. package/session-convert.mjs +179 -0
  78. package/session-emission-version.json +1 -0
  79. package/session-file.mjs +11 -1
  80. package/session-follower.mjs +17 -5
  81. package/session-fresh-id.mjs +15 -0
  82. package/session-hermes-import.mjs +61 -0
  83. package/session-hermes.mjs +1 -1
  84. package/session-import.mjs +37 -0
  85. package/session-inspect.d.ts +18 -0
  86. package/session-inspect.mjs +122 -0
  87. package/session-machine-tree.d.ts +33 -0
  88. package/session-machine-tree.mjs +274 -0
  89. package/session-materialize-files.mjs +78 -0
  90. package/session-materialize-opencode.mjs +137 -0
  91. package/session-materialize.mjs +186 -0
  92. package/session-message.mjs +85 -0
  93. package/session-model.d.ts +9 -0
  94. package/session-model.mjs +1 -0
  95. package/session-opencode-store.mjs +39 -3
  96. package/session-path.mjs +6 -3
  97. package/session-pi-content.mjs +7 -2
  98. package/session-read.d.ts +134 -2
  99. package/session-read.mjs +86 -9
  100. package/session-recorded-config.mjs +3 -2
  101. package/session-records.mjs +12 -6
  102. package/session-residue-store.mjs +90 -0
  103. package/session-residue.mjs +150 -0
  104. package/session-resume-instructions.mjs +1 -1
  105. package/session-saved-read.mjs +105 -0
  106. package/session-transfer.mjs +141 -0
  107. package/session-views.mjs +1 -0
  108. package/session-write-claude.mjs +135 -0
  109. package/session-write-codex.mjs +146 -0
  110. package/session-write-gemini.mjs +77 -0
  111. package/session-write-goose.mjs +99 -0
  112. package/session-write-grok.mjs +70 -0
  113. package/session-write-native.mjs +41 -0
  114. package/session-write-opencode.mjs +197 -0
  115. package/session-write-pi.mjs +130 -0
  116. package/session-write-residue.mjs +19 -0
  117. package/session-write-utils.mjs +77 -0
  118. package/session-write.d.ts +10 -0
  119. package/session-write.mjs +37 -0
  120. package/skill-toml.mjs +80 -0
  121. package/skills-control.mjs +193 -0
  122. package/skills-list.mjs +237 -0
  123. package/source-files.mjs +12 -2
  124. package/sqlite-reader.mjs +134 -18
  125. package/support-registry.json +402 -0
  126. package/support-report.mjs +69 -0
  127. package/trigger-records.mjs +77 -0
  128. package/triggers-read.mjs +67 -0
  129. package/workflow-read.mjs +165 -0
package/sqlite-reader.mjs CHANGED
@@ -1,49 +1,165 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
- import { fileFingerprint } from './file-io.mjs';
2
+ import { mkdtemp, open, realpath, rm } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join, resolve } from 'node:path';
5
+ import { fileFingerprint, sameFingerprint, withSessionFile } from './file-io.mjs';
3
6
  import { sourceStat } from './store-registry.mjs';
4
7
  import { verifySources } from './source-files.mjs';
5
8
  import { parseNativeJson } from './native-json.mjs';
9
+ import { SESSION_UNREADABLE } from './session-file.mjs';
6
10
 
7
- /** Only DB and WAL mutations affect the stored read mark. SHM is SQLite's
8
- * reader coordination file and never schedules transcript/index work. */
9
- export async function storeFingerprints(path) {
11
+ const SNAPSHOT_ROW_BYTES = 256 * 1024 * 1024;
12
+ const snapshots = new WeakMap();
13
+ function chargeRow(snapshot, row) {
14
+ if (row == null) return row;
15
+ let bytes = 64;
16
+ for (const [name, value] of Object.entries(row)) {
17
+ bytes += 32 + name.length * 2;
18
+ if (typeof value === 'string') bytes += value.length * 2;
19
+ else if (ArrayBuffer.isView(value)) bytes += value.byteLength;
20
+ else if (value instanceof ArrayBuffer) bytes += value.byteLength;
21
+ else bytes += 8;
22
+ }
23
+ if (bytes > SNAPSHOT_ROW_BYTES - snapshot.bytes) throw Object.assign(new Error(`${snapshot.path} exceeds the foreign-store snapshot row budget (${SNAPSHOT_ROW_BYTES} charged bytes); no partial session is returned`), { code: 'EFBIG', rpcCode: SESSION_UNREADABLE });
24
+ snapshot.bytes += bytes;
25
+ return row;
26
+ }
27
+ function admittedStatement(prepared, snapshot) {
28
+ const iterate = function* (...args) { for (const row of prepared.iterate(...args)) yield chargeRow(snapshot, row); };
29
+ return new Proxy(prepared, { get(target, name) {
30
+ if (name === 'iterate') return iterate;
31
+ if (name === 'all') return (...args) => Array.from(iterate(...args));
32
+ if (name === 'get') return (...args) => chargeRow(snapshot, target.get(...args));
33
+ const value = Reflect.get(target, name, target);
34
+ return typeof value === 'function' ? value.bind(target) : value;
35
+ } });
36
+ }
37
+
38
+ const missing = (error) => error.code === 'ENOENT' || error.code === 'ENOTDIR';
39
+ const changed = (path) => Object.assign(new Error(`${path} changed during its foreign-store snapshot; previous index rows are retained`), { code: 'ESTALE' });
40
+
41
+ /** Native aliases follow their database target, including its adjacent WAL
42
+ * and rollback journal. SHM is coordination, not transcript source content. */
43
+ async function storeSources(path) {
44
+ let target;
45
+ try { target = await realpath(path); }
46
+ catch (error) { if (!missing(error)) throw error; target = resolve(path); }
10
47
  const sources = [];
11
- for (const file of [path, `${path}-wal`]) {
48
+ for (const file of [path, `${target}-wal`, `${target}-journal`]) {
12
49
  const info = await sourceStat(file);
50
+ if (info && !info.isFile()) throw Object.assign(new Error(`${file} is not a regular foreign-store file`), { code: 'EINVAL' });
13
51
  sources.push(info ? { path: file, fingerprint: fileFingerprint(info) } : { path: file, missing: true });
14
52
  }
15
- return sources;
53
+ return { target, sources };
54
+ }
55
+
56
+ export async function storeFingerprints(path) {
57
+ return (await storeSources(path)).sources;
58
+ }
59
+
60
+ async function verifyStoreSources(path, target, sources) {
61
+ await verifySources(sources);
62
+ let current;
63
+ try { current = await realpath(path); } catch (error) { if (!missing(error)) throw error; }
64
+ if (current !== target) throw changed(path);
65
+ }
66
+
67
+ async function copySource(source, destination, metrics, snapshot) {
68
+ await withSessionFile(source.path, async (reader) => {
69
+ if (!sameFingerprint(source.fingerprint, reader.fingerprint)) throw changed(source.path);
70
+ const output = await open(destination, 'wx', 0o600);
71
+ snapshot.outputs.add(output);
72
+ let copyError;
73
+ try {
74
+ for (let offset = 0; offset < reader.size; offset += 64 * 1024)
75
+ await output.writeFile(await reader.read(offset, Math.min(64 * 1024, reader.size - offset)));
76
+ } catch (error) { copyError = error; throw error; }
77
+ finally {
78
+ try { await output.close(); snapshot.outputs.delete(output); }
79
+ catch (error) {
80
+ if (copyError) throw new AggregateError([copyError, error], `${source.path} snapshot copy and close failed`);
81
+ throw error;
82
+ }
83
+ }
84
+ }, metrics);
85
+ }
86
+
87
+ // A failed close/removal keeps its exact owner and private path. A later read
88
+ // settles retained cleanup before admitting another snapshot, rather than
89
+ // forgetting a connection or treating uncertain cleanup as successful.
90
+ const retainedCleanup = new Map();
91
+ let cleanupDrain;
92
+ async function cleanupSnapshot(snapshot) {
93
+ for (const output of snapshot.outputs) {
94
+ if (output.fd !== -1) await output.close();
95
+ snapshot.outputs.delete(output);
96
+ }
97
+ if (snapshot.db) {
98
+ if (snapshot.db.isOpen !== false) snapshot.db.close();
99
+ snapshots.delete(snapshot.db);
100
+ snapshot.db = null;
101
+ }
102
+ await rm(snapshot.directory, { recursive: true, force: true });
103
+ }
104
+
105
+ async function settleCleanup() {
106
+ if (!retainedCleanup.size) return;
107
+ cleanupDrain ??= (async () => {
108
+ for (const [directory, snapshot] of retainedCleanup) {
109
+ try { await cleanupSnapshot(snapshot); retainedCleanup.delete(directory); }
110
+ catch (error) { throw Object.assign(new Error(`foreign-store snapshot cleanup remains incomplete at ${directory}`, { cause: error }), { code: 'FOREIGN_STORE_CLEANUP' }); }
111
+ }
112
+ })().finally(() => { cleanupDrain = null; });
113
+ await cleanupDrain;
16
114
  }
17
115
 
18
- /** One read-only connection and one synchronous snapshot. A changed DB/WAL
19
- * refuses the commit unit, leaving the owner's prior rows and retry visible.
116
+ /** SQLite opens only owned private copies, never a mutable foreign pathname.
117
+ * Source descriptors use nonblocking admission and exact initial/final stamps;
118
+ * copies stream in 64 KiB ranges. A changed DB/WAL/journal refuses the whole
119
+ * unit. Private SHM/locks belong to this snapshot, with no foreign writes.
20
120
  * No foreign writer, journal-mode setter or extension loading is permitted. */
21
121
  export async function readStore(path, work, metrics = {}) {
22
- const sources = await storeFingerprints(path);
122
+ await settleCleanup();
123
+ const { target, sources } = await storeSources(path);
23
124
  if (sources[0].missing) throw Object.assign(new Error(`${path} is not present`), { code: 'ENOENT' });
24
- const db = new DatabaseSync(path, { readOnly: true, allowExtension: false });
25
- metrics.stores_read = (metrics.stores_read ?? 0) + 1;
26
- let result;
125
+ const snapshot = { directory: await mkdtemp(join(tmpdir(), 'supercode-store-')), db: null, outputs: new Set() };
126
+ let readError;
27
127
  try {
128
+ const privatePath = join(snapshot.directory, 'source.db');
129
+ for (const [index, suffix] of ['', '-wal', '-journal'].entries())
130
+ if (!sources[index].missing) await copySource(sources[index], `${privatePath}${suffix}`, metrics, snapshot);
131
+ await verifyStoreSources(path, target, sources);
132
+ const db = snapshot.db = new DatabaseSync(privatePath, { readOnly: true, allowExtension: false });
133
+ snapshots.set(db, { path, bytes: 0 });
134
+ metrics.stores_read = (metrics.stores_read ?? 0) + 1;
28
135
  db.exec('PRAGMA busy_timeout=0; BEGIN');
29
- result = work(db);
136
+ const result = work(db);
30
137
  if (result != null && typeof result.then === 'function') throw new Error('a foreign-store snapshot must be synchronous');
31
138
  db.exec('COMMIT');
32
- } finally { db.close(); }
33
- await verifySources(sources);
34
- return { result, sources };
139
+ await verifyStoreSources(path, target, sources);
140
+ return { result, sources };
141
+ } catch (error) { readError = error; throw error; }
142
+ finally {
143
+ try { await cleanupSnapshot(snapshot); }
144
+ catch (error) {
145
+ retainedCleanup.set(snapshot.directory, snapshot);
146
+ const failures = readError ? [readError, error] : [error];
147
+ throw Object.assign(new AggregateError(failures, `foreign-store snapshot cleanup incomplete at ${snapshot.directory}`), { code: 'FOREIGN_STORE_CLEANUP' });
148
+ }
149
+ }
35
150
  }
36
151
 
37
152
  export function statement(db, sql) {
38
153
  const prepared = db.prepare(sql);
39
154
  prepared.setReadBigInts(true);
40
- return prepared;
155
+ const snapshot = snapshots.get(db);
156
+ return snapshot ? admittedStatement(prepared, snapshot) : prepared;
41
157
  }
42
158
 
43
159
  export function tableColumns(db, table) {
44
160
  // Callers provide only these fixed native schema identifiers.
45
161
  if (!['session', 'sessions', 'messages'].includes(table)) throw new Error('unknown catalog schema table');
46
- return new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name));
162
+ return new Set(statement(db, `PRAGMA table_info(${table})`).all().map((row) => row.name));
47
163
  }
48
164
 
49
165
  export function text(value, nullable = false) {
@@ -0,0 +1,402 @@
1
+ {
2
+ "schema": "supercode.support-registry.v1",
3
+ "harnesses": [
4
+ {
5
+ "id": "claude-code",
6
+ "display_name": "Claude Code",
7
+ "native": {
8
+ "discover": "built_in",
9
+ "load": "built_in",
10
+ "follow": "built_in",
11
+ "import": "built_in",
12
+ "export": "built_in"
13
+ },
14
+ "runtime": {
15
+ "implementation": "built_in",
16
+ "protocol": "claude-stream-json",
17
+ "default_launch": {
18
+ "program": "claude",
19
+ "arguments": [
20
+ "--print",
21
+ "--input-format",
22
+ "stream-json",
23
+ "--output-format",
24
+ "stream-json",
25
+ "--verbose",
26
+ "--permission-prompt-tool",
27
+ "stdio"
28
+ ],
29
+ "env": {}
30
+ },
31
+ "capabilities": {
32
+ "start_session": true,
33
+ "resume_session": true,
34
+ "attach_existing_process": false,
35
+ "send_input": true,
36
+ "stream_events": true,
37
+ "interrupt": true,
38
+ "steer": true,
39
+ "respond_to_requests": true
40
+ }
41
+ }
42
+ },
43
+ {
44
+ "id": "codex",
45
+ "display_name": "Codex",
46
+ "native": {
47
+ "discover": "built_in",
48
+ "load": "built_in",
49
+ "follow": "built_in",
50
+ "import": "built_in",
51
+ "export": "built_in"
52
+ },
53
+ "runtime": {
54
+ "implementation": "built_in",
55
+ "protocol": "codex-app-server-jsonl",
56
+ "default_launch": {
57
+ "program": "codex",
58
+ "arguments": [
59
+ "app-server"
60
+ ],
61
+ "env": {}
62
+ },
63
+ "capabilities": {
64
+ "start_session": true,
65
+ "resume_session": true,
66
+ "attach_existing_process": false,
67
+ "send_input": true,
68
+ "stream_events": true,
69
+ "interrupt": true,
70
+ "steer": true,
71
+ "respond_to_requests": true
72
+ }
73
+ }
74
+ },
75
+ {
76
+ "id": "opencode",
77
+ "display_name": "OpenCode",
78
+ "native": {
79
+ "discover": "built_in",
80
+ "load": "built_in",
81
+ "follow": "built_in",
82
+ "import": "built_in",
83
+ "export": "built_in"
84
+ },
85
+ "runtime": {
86
+ "implementation": "built_in",
87
+ "protocol": "opencode-http-sse",
88
+ "default_launch": {
89
+ "program": "opencode",
90
+ "arguments": [
91
+ "serve"
92
+ ],
93
+ "env": {}
94
+ },
95
+ "capabilities": {
96
+ "start_session": true,
97
+ "resume_session": true,
98
+ "attach_existing_process": false,
99
+ "send_input": true,
100
+ "stream_events": true,
101
+ "interrupt": true,
102
+ "steer": false,
103
+ "respond_to_requests": true
104
+ }
105
+ }
106
+ },
107
+ {
108
+ "id": "pi",
109
+ "display_name": "Pi",
110
+ "native": {
111
+ "discover": "built_in",
112
+ "load": "built_in",
113
+ "follow": "built_in",
114
+ "import": "built_in",
115
+ "export": "built_in"
116
+ },
117
+ "runtime": {
118
+ "implementation": "built_in",
119
+ "protocol": "pi-rpc-jsonl",
120
+ "default_launch": {
121
+ "program": "pi",
122
+ "arguments": [
123
+ "--mode",
124
+ "rpc"
125
+ ],
126
+ "env": {}
127
+ },
128
+ "capabilities": {
129
+ "start_session": true,
130
+ "resume_session": true,
131
+ "attach_existing_process": false,
132
+ "send_input": true,
133
+ "stream_events": true,
134
+ "interrupt": true,
135
+ "steer": false,
136
+ "respond_to_requests": true
137
+ }
138
+ }
139
+ },
140
+ {
141
+ "id": "runhuman",
142
+ "display_name": "Runhuman",
143
+ "native": {
144
+ "discover": "absent",
145
+ "load": "absent",
146
+ "follow": "absent",
147
+ "import": "absent",
148
+ "export": "absent"
149
+ },
150
+ "runtime": {
151
+ "implementation": "built_in",
152
+ "protocol": "rhh-rpc-jsonl",
153
+ "default_launch": {
154
+ "program": "rhh",
155
+ "arguments": [
156
+ "--mode",
157
+ "rpc"
158
+ ],
159
+ "env": {}
160
+ },
161
+ "capabilities": {
162
+ "start_session": true,
163
+ "resume_session": true,
164
+ "attach_existing_process": false,
165
+ "send_input": true,
166
+ "stream_events": true,
167
+ "interrupt": true,
168
+ "steer": false,
169
+ "respond_to_requests": true
170
+ }
171
+ }
172
+ },
173
+ {
174
+ "id": "grok",
175
+ "display_name": "Grok",
176
+ "native": {
177
+ "discover": "built_in",
178
+ "load": "built_in",
179
+ "follow": "built_in",
180
+ "import": "built_in",
181
+ "export": "built_in"
182
+ },
183
+ "runtime": {
184
+ "implementation": "generic_protocol",
185
+ "protocol": "acp-v1-jsonrpc",
186
+ "default_launch": {
187
+ "program": "grok",
188
+ "arguments": [
189
+ "--sandbox",
190
+ "workspace",
191
+ "agent",
192
+ "--no-leader",
193
+ "stdio"
194
+ ],
195
+ "env": {}
196
+ },
197
+ "capabilities": {
198
+ "start_session": true,
199
+ "resume_session": true,
200
+ "attach_existing_process": false,
201
+ "send_input": true,
202
+ "stream_events": true,
203
+ "interrupt": true,
204
+ "steer": false,
205
+ "respond_to_requests": true
206
+ }
207
+ }
208
+ },
209
+ {
210
+ "id": "gemini",
211
+ "display_name": "Gemini CLI",
212
+ "native": {
213
+ "discover": "built_in",
214
+ "load": "built_in",
215
+ "follow": "built_in",
216
+ "import": "built_in",
217
+ "export": "built_in"
218
+ },
219
+ "runtime": {
220
+ "implementation": "generic_protocol",
221
+ "protocol": "acp-v1-jsonrpc",
222
+ "default_launch": {
223
+ "program": "gemini",
224
+ "arguments": [
225
+ "--experimental-acp"
226
+ ],
227
+ "env": {}
228
+ },
229
+ "capabilities": {
230
+ "start_session": true,
231
+ "resume_session": true,
232
+ "attach_existing_process": false,
233
+ "send_input": true,
234
+ "stream_events": true,
235
+ "interrupt": true,
236
+ "steer": false,
237
+ "respond_to_requests": true
238
+ }
239
+ }
240
+ },
241
+ {
242
+ "id": "goose",
243
+ "display_name": "Goose",
244
+ "native": {
245
+ "discover": "built_in",
246
+ "load": "built_in",
247
+ "follow": "built_in",
248
+ "import": "built_in",
249
+ "export": "built_in"
250
+ },
251
+ "runtime": {
252
+ "implementation": "generic_protocol",
253
+ "protocol": "acp-v1-jsonrpc",
254
+ "default_launch": {
255
+ "program": "goose",
256
+ "arguments": [
257
+ "acp"
258
+ ],
259
+ "env": {}
260
+ },
261
+ "capabilities": {
262
+ "start_session": true,
263
+ "resume_session": true,
264
+ "attach_existing_process": false,
265
+ "send_input": true,
266
+ "stream_events": true,
267
+ "interrupt": true,
268
+ "steer": false,
269
+ "respond_to_requests": true
270
+ }
271
+ }
272
+ },
273
+ {
274
+ "id": "hermes",
275
+ "display_name": "Hermes Agent",
276
+ "native": {
277
+ "discover": "built_in",
278
+ "load": "built_in",
279
+ "follow": "absent",
280
+ "import": "absent",
281
+ "export": "generic_protocol"
282
+ },
283
+ "runtime": {
284
+ "implementation": "generic_protocol",
285
+ "protocol": "acp-v1-jsonrpc",
286
+ "default_launch": {
287
+ "program": "hermes-acp",
288
+ "arguments": [],
289
+ "env": {}
290
+ },
291
+ "capabilities": {
292
+ "start_session": true,
293
+ "resume_session": true,
294
+ "attach_existing_process": false,
295
+ "send_input": true,
296
+ "stream_events": true,
297
+ "interrupt": true,
298
+ "steer": false,
299
+ "respond_to_requests": true
300
+ }
301
+ }
302
+ },
303
+ {
304
+ "id": "openclaw",
305
+ "display_name": "OpenClaw",
306
+ "native": {
307
+ "discover": "built_in",
308
+ "load": "built_in",
309
+ "follow": "absent",
310
+ "import": "absent",
311
+ "export": "absent"
312
+ },
313
+ "runtime": {
314
+ "implementation": "generic_protocol",
315
+ "protocol": "acp-v1-jsonrpc",
316
+ "default_launch": {
317
+ "program": "openclaw",
318
+ "arguments": [
319
+ "acp"
320
+ ],
321
+ "env": {}
322
+ },
323
+ "connect_launch": {
324
+ "config_path": "~/.openclaw/openclaw.json",
325
+ "address_pointer": "/gateway/remote/url",
326
+ "port_pointer": "/gateway/port",
327
+ "default_address": "ws://127.0.0.1:18789",
328
+ "auth_pointer": "/gateway/auth/token",
329
+ "protocol": "acp-v1-jsonrpc"
330
+ },
331
+ "capabilities": {
332
+ "start_session": true,
333
+ "resume_session": true,
334
+ "attach_existing_process": false,
335
+ "send_input": true,
336
+ "stream_events": true,
337
+ "interrupt": true,
338
+ "steer": false,
339
+ "respond_to_requests": true
340
+ }
341
+ }
342
+ },
343
+ {
344
+ "id": "orchestrator",
345
+ "display_name": "Orchestrator",
346
+ "native": {
347
+ "discover": "absent",
348
+ "load": "absent",
349
+ "follow": "absent",
350
+ "import": "absent",
351
+ "export": "absent"
352
+ },
353
+ "runtime": {
354
+ "implementation": "absent",
355
+ "protocol": "none",
356
+ "default_launch": null,
357
+ "capabilities": {
358
+ "start_session": false,
359
+ "resume_session": false,
360
+ "attach_existing_process": false,
361
+ "send_input": false,
362
+ "stream_events": false,
363
+ "interrupt": false,
364
+ "steer": false,
365
+ "respond_to_requests": false
366
+ }
367
+ }
368
+ },
369
+ {
370
+ "id": "supercode",
371
+ "display_name": "Volter Harness",
372
+ "native": {
373
+ "discover": "built_in",
374
+ "load": "built_in",
375
+ "follow": "built_in",
376
+ "import": "built_in",
377
+ "export": "built_in"
378
+ },
379
+ "runtime": {
380
+ "implementation": "generic_protocol",
381
+ "protocol": "acp-v1-jsonrpc",
382
+ "default_launch": {
383
+ "program": "supercode",
384
+ "arguments": [
385
+ "acp"
386
+ ],
387
+ "env": {}
388
+ },
389
+ "capabilities": {
390
+ "start_session": true,
391
+ "resume_session": true,
392
+ "attach_existing_process": false,
393
+ "send_input": true,
394
+ "stream_events": true,
395
+ "interrupt": true,
396
+ "steer": false,
397
+ "respond_to_requests": true
398
+ }
399
+ }
400
+ }
401
+ ]
402
+ }
@@ -0,0 +1,69 @@
1
+ // Implementation facts, not a verdict that a vendor works or is installed.
2
+ import { readFileSync } from 'node:fs';
3
+ import { isAbsolute, toNamespacedPath } from 'node:path';
4
+ import { grokHomeEnvironment } from './session-resume-instructions.mjs';
5
+
6
+ const table = JSON.parse(readFileSync(new URL('./support-registry.json', import.meta.url), 'utf8'));
7
+ const concepts = ['scheduled_job', 'run', 'conversation', 'pending_request', 'profile', 'skills', 'memory', 'delivery_target', 'channel', 'routing', 'inbound_trigger', 'gateway_health'];
8
+ const gateways = ['hermes', 'openclaw', 'orchestrator'];
9
+ const lifecycle = { codex: ['archive', 'delete'], opencode: ['archive', 'delete'], hermes: ['reset', 'delete'], openclaw: ['new', 'reset'], orchestrator: ['new', 'reset'], supercode: ['archive', 'delete'] };
10
+ const methods = (noun, verbs) => verbs.map(verb => `harness.v1.${noun}.${verb}`);
11
+
12
+ export function supportOrchestration(descriptor) {
13
+ const id = descriptor.id, gateway = gateways.includes(id), jobs = gateway || id === 'claude-code';
14
+ return { concepts: concepts.map(concept => {
15
+ let observed = 'absent', controlled = 'absent', backing = [];
16
+ const read = list => { observed = 'built_in'; backing = list; };
17
+ const control = list => { read(list); controlled = 'built_in'; };
18
+ switch (concept) {
19
+ case 'scheduled_job':
20
+ if (gateway) control(methods('jobs', ['list', 'get', 'create', 'update', 'pause', 'resume', 'run', 'delete']));
21
+ else if (jobs) read(methods('jobs', ['list', 'get']));
22
+ break;
23
+ case 'run': if (gateway) read(methods('runs', ['list', 'get'])); break;
24
+ case 'conversation':
25
+ if (descriptor.native.load === 'built_in') {
26
+ const verbs = lifecycle[id] ?? [];
27
+ read(methods('sessions', ['discover', 'load', ...verbs]));
28
+ if (verbs.length) controlled = 'built_in';
29
+ } else if (id === 'orchestrator') control(methods('sessions', ['discover', ...lifecycle[id]]));
30
+ break;
31
+ case 'pending_request': if (descriptor.runtime.capabilities.respond_to_requests) control(['harness.v1.approvals.list', 'harness.v1.approvals.resolve', 'harness.v1.runtimes.respond']); break;
32
+ case 'profile':
33
+ if (gateway) control(methods('profiles', ['list', 'get', 'create', 'delete']));
34
+ else if (['codex', 'supercode'].includes(id)) read(methods('profiles', ['list', 'get']));
35
+ break;
36
+ case 'skills': if (['claude-code', 'codex', 'opencode', 'pi', 'hermes', 'openclaw'].includes(id)) control(methods('skills', ['list', 'install', 'remove'])); break;
37
+ case 'memory': if (['claude-code', 'hermes', 'openclaw'].includes(id)) read(methods('memory', ['show', 'search'])); break;
38
+ case 'delivery_target': if (jobs) read([...methods('jobs', ['list', 'get']), ...(gateway ? methods('runs', ['list', 'get']) : [])]); break;
39
+ case 'channel': if (gateway) read(methods('channels', ['list', 'status'])); break;
40
+ case 'routing': if (gateway) read(methods('routes', ['list'])); break;
41
+ case 'inbound_trigger': if (gateway) read(methods('triggers', ['list'])); break;
42
+ case 'gateway_health': if (gateway) read(['harness.v1.harnesses.list']); break;
43
+ }
44
+ return { concept, observed, controlled, ...(backing.length ? { methods: backing } : {}) };
45
+ }) };
46
+ }
47
+
48
+ export function supportRegistryReport({ env = process.env, cwd = process.cwd() } = {}) {
49
+ const report = structuredClone(table);
50
+ for (const descriptor of report.harnesses) {
51
+ if (descriptor.id === 'codex') {
52
+ const program = env.SUPERCODE_CODEX_BIN;
53
+ // The native registry does not trim, stat or substitute a configured
54
+ // executable. An invalid relative override means no default launch.
55
+ const absolute = typeof program === 'string' && isAbsolute(program)
56
+ && (process.platform !== 'win32' || /^(?:[A-Za-z]:[\\/]|[\\/]{2})/.test(program));
57
+ descriptor.runtime.default_launch = program ? absolute ? { program, arguments: ['app-server'], env: {} } : null
58
+ : { program: 'codex', arguments: ['app-server'], env: {} };
59
+ } else if (descriptor.id === 'grok') {
60
+ const home = grokHomeEnvironment(env, cwd);
61
+ // Rust canonicalize keeps the Windows extended path prefix in this
62
+ // inventory's environment value, including an owned junction target.
63
+ if (process.platform === 'win32' && home.GROK_HOME != null) home.GROK_HOME = toNamespacedPath(home.GROK_HOME);
64
+ descriptor.runtime.default_launch.env = { GROK_AGENT_DASHBOARD: '0', ...home };
65
+ }
66
+ descriptor.orchestration = supportOrchestration(descriptor);
67
+ }
68
+ return report;
69
+ }