@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.
- package/LICENSE-diffy-MIT +23 -0
- package/README.md +422 -2
- package/channel-records.mjs +71 -0
- package/channel-session-counts.mjs +81 -0
- package/channels-read.mjs +90 -0
- package/corpus-audit-records.mjs +192 -0
- package/corpus-audit-render.mjs +47 -0
- package/corpus-audit-schema.mjs +89 -0
- package/corpus-audit.d.ts +16 -0
- package/corpus-audit.mjs +132 -0
- package/file-io.mjs +21 -2
- package/harness-settings.mjs +19 -0
- package/hermes-fire-links.mjs +135 -0
- package/hermes-fires.mjs +106 -0
- package/hermes-read-home.d.ts +3 -0
- package/hermes-read-home.mjs +235 -0
- package/hermes-read-store.mjs +49 -0
- package/hermes-read-validation.mjs +170 -0
- package/hermes-read-workflow.mjs +164 -0
- package/hermes-read-yaml.mjs +68 -0
- package/hermes-store.mjs +1 -1
- package/homes.mjs +2 -2
- package/index.d.ts +14 -1
- package/index.mjs +1 -1
- package/inspection-builtin-tools.json +261 -0
- package/inspection-config-fields.json +765 -0
- package/inspection-schemas.d.ts +4 -0
- package/inspection-schemas.mjs +117 -0
- package/job-notepad-read.mjs +97 -0
- package/jobs-control.mjs +250 -0
- package/jobs-read.mjs +198 -0
- package/jsonl.mjs +4 -3
- package/memory-show.mjs +187 -0
- package/metadata.mjs +45 -3
- package/native-json.mjs +46 -3
- package/ontology-contracts.d.ts +5 -0
- package/ontology-contracts.mjs +29 -0
- package/ontology-operations.json +198 -0
- package/ontology-schemas.json +7946 -0
- package/openclaw-jobs-read.mjs +117 -0
- package/openclaw-profile-home.mjs +142 -0
- package/orchestrator-read-records.mjs +112 -0
- package/orchestrator-store.mjs +21 -9
- package/package.json +59 -4
- package/parity-ledger.json +6984 -0
- package/parity-orchestration.json +5538 -0
- package/parity-report.d.ts +7 -0
- package/parity-report.mjs +46 -0
- package/preset-profiles.mjs +27 -0
- package/profile-command.mjs +32 -0
- package/profile-orchestrator-door.mjs +104 -0
- package/profile-paths.mjs +25 -0
- package/profiles-control.mjs +98 -0
- package/profiles-read.mjs +119 -0
- package/reduction-attribution.mjs +54 -0
- package/reduction-diff.mjs +165 -0
- package/reduction-log.mjs +177 -0
- package/reduction-view.mjs +145 -0
- package/retained-projection.mjs +9 -0
- package/routes-read.mjs +103 -0
- package/runs-read.mjs +224 -0
- package/session-activity.d.ts +23 -0
- package/session-activity.mjs +124 -0
- package/session-append-scan.mjs +27 -17
- package/session-branch-handoff.mjs +115 -0
- package/session-claude-graph.mjs +29 -7
- package/session-claude-index.mjs +732 -114
- package/session-claude-runtime.d.ts +28 -0
- package/session-claude-runtime.mjs +171 -6
- package/session-claude-scan-store.mjs +164 -0
- package/session-claude.mjs +9 -0
- package/session-codex-activity.d.ts +6 -0
- package/session-codex-activity.mjs +120 -0
- package/session-codex-window.mjs +24 -4
- package/session-config-scan.mjs +141 -0
- package/session-convert.d.ts +33 -0
- package/session-convert.mjs +179 -0
- package/session-emission-version.json +1 -0
- package/session-file.mjs +11 -1
- package/session-follower.mjs +17 -5
- package/session-fresh-id.mjs +15 -0
- package/session-hermes-import.mjs +61 -0
- package/session-hermes.mjs +1 -1
- package/session-import.mjs +37 -0
- package/session-inspect.d.ts +18 -0
- package/session-inspect.mjs +122 -0
- package/session-machine-tree.d.ts +33 -0
- package/session-machine-tree.mjs +274 -0
- package/session-materialize-files.mjs +78 -0
- package/session-materialize-opencode.mjs +137 -0
- package/session-materialize.mjs +186 -0
- package/session-message.mjs +85 -0
- package/session-model.d.ts +9 -0
- package/session-model.mjs +1 -0
- package/session-opencode-store.mjs +39 -3
- package/session-path.mjs +6 -3
- package/session-pi-content.mjs +7 -2
- package/session-read.d.ts +134 -2
- package/session-read.mjs +86 -9
- package/session-recorded-config.mjs +3 -2
- package/session-records.mjs +12 -6
- package/session-residue-store.mjs +90 -0
- package/session-residue.mjs +150 -0
- package/session-resume-instructions.mjs +1 -1
- package/session-saved-read.mjs +105 -0
- package/session-transfer.mjs +141 -0
- package/session-views.mjs +1 -0
- package/session-write-claude.mjs +135 -0
- package/session-write-codex.mjs +146 -0
- package/session-write-gemini.mjs +77 -0
- package/session-write-goose.mjs +99 -0
- package/session-write-grok.mjs +70 -0
- package/session-write-native.mjs +41 -0
- package/session-write-opencode.mjs +197 -0
- package/session-write-pi.mjs +130 -0
- package/session-write-residue.mjs +19 -0
- package/session-write-utils.mjs +77 -0
- package/session-write.d.ts +10 -0
- package/session-write.mjs +37 -0
- package/skill-toml.mjs +80 -0
- package/skills-control.mjs +193 -0
- package/skills-list.mjs +237 -0
- package/source-files.mjs +12 -2
- package/sqlite-reader.mjs +134 -18
- package/support-registry.json +402 -0
- package/support-report.mjs +69 -0
- package/trigger-records.mjs +77 -0
- package/triggers-read.mjs +67 -0
- package/workflow-read.mjs +165 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// A continuation chooses a fresh native identity and destination cwd. Source
|
|
2
|
+
// recovery is cached first; it is never installed as a vendor store file.
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { resolveHarnessHomes } from './homes.mjs';
|
|
5
|
+
import { parseNativeJson, stringifyNativeJson, stringifyNativeJsonPretty, swapRemoveNativeJsonMember } from './native-json.mjs';
|
|
6
|
+
import { nativeTrim } from './text.mjs';
|
|
7
|
+
import { readSessionDocument } from './session-document.mjs';
|
|
8
|
+
import { fromNativeStr } from './session-native.mjs';
|
|
9
|
+
import { toSessionJsonlSpliced } from './session-write.mjs';
|
|
10
|
+
import { createResidueStore } from './session-residue-store.mjs';
|
|
11
|
+
import { materializeOpenCode } from './session-materialize-opencode.mjs';
|
|
12
|
+
import { isObject, nativeJoin, operationPath, canonicalPath, safeArtifactPath, freshSessionId, writeExclusive, containedDirectory, samePath } from './session-materialize-files.mjs';
|
|
13
|
+
|
|
14
|
+
const invalid = message => { throw Object.assign(new Error(message), { rpcCode: -32602 }); };
|
|
15
|
+
const string = (value, name) => { if (typeof value !== 'string') invalid(`${name} must be a string`); return value; };
|
|
16
|
+
const homesFields = ['claude_code', 'codex', 'pi', 'opencode', 'grok', 'gemini', 'goose', 'supercode', 'openclaw', 'hermes', 'orchestrator'];
|
|
17
|
+
function struct(value, fields, name, minimum = fields.length) {
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
if (value.length < minimum || value.length > fields.length) invalid(`${name} positional fields require ${minimum} to ${fields.length} values`);
|
|
20
|
+
return Object.fromEntries(value.map((value, index) => [fields[index], value]));
|
|
21
|
+
}
|
|
22
|
+
if (!isObject(value)) invalid(`${name} must be an object`);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function fidelity(value) {
|
|
26
|
+
if (isObject(value)) {
|
|
27
|
+
const keys = Object.keys(value);
|
|
28
|
+
if (keys.length !== 1 || value[keys[0]] !== null) invalid('artifact.fidelity must be a fidelity variant');
|
|
29
|
+
value = keys[0];
|
|
30
|
+
}
|
|
31
|
+
if (!['semantic', 'value_lossless', 'byte_lossless'].includes(value)) invalid('artifact.fidelity must be a fidelity variant');
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function parseSessionMaterializeParams(params, { env = process.env } = {}) {
|
|
36
|
+
params = struct(params, ['artifact', 'cwd', 'homes'], 'materialize params', 2);
|
|
37
|
+
const supplied = struct(params.artifact, ['target_harness', 'session_id', 'content', 'files', 'fidelity'], 'artifact');
|
|
38
|
+
if (!Array.isArray(supplied.files)) invalid('artifact.files must be an array');
|
|
39
|
+
const artifact = { target_harness: string(supplied.target_harness, 'artifact.target_harness'),
|
|
40
|
+
session_id: supplied.session_id == null ? null : string(supplied.session_id, 'artifact.session_id'),
|
|
41
|
+
content: string(supplied.content, 'artifact.content'), files: supplied.files.map(value => {
|
|
42
|
+
const file = struct(value, ['path', 'content', 'role'], 'artifact file');
|
|
43
|
+
return { path: string(file.path, 'artifact file.path'), content: string(file.content, 'artifact file.content'), role: string(file.role, 'artifact file.role') };
|
|
44
|
+
}), fidelity: fidelity(supplied.fidelity) };
|
|
45
|
+
const homes = resolveHarnessHomes(env, { joinPath: nativeJoin });
|
|
46
|
+
if (Object.hasOwn(params, 'homes')) {
|
|
47
|
+
const suppliedHomes = struct(params.homes, homesFields, 'homes', 0);
|
|
48
|
+
for (const key of homesFields) if (Object.hasOwn(suppliedHomes, key)) homes[key] = string(suppliedHomes[key], `homes.${key}`);
|
|
49
|
+
}
|
|
50
|
+
return { artifact, cwd: string(params.cwd, 'cwd'), homes };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function validateMaterialization(artifact, cwd, harness, companions, context) {
|
|
54
|
+
if (artifact.fidelity === 'byte_lossless') throw new Error('fresh native identity materialization cannot meet byte_lossless fidelity');
|
|
55
|
+
const primary = artifact.files.filter(file => file.role === 'primary');
|
|
56
|
+
if (primary.length !== 1 || primary[0].content !== artifact.content) throw new Error('native artifact must contain one matching primary file');
|
|
57
|
+
if (artifact.files.some(file => !safeArtifactPath(file.path))) throw new Error('native artifact contains an unsafe relative filename');
|
|
58
|
+
const extra = artifact.files.find(file => file.role !== 'primary' && file.role !== 'source_recovery' && !companions.includes(file.path));
|
|
59
|
+
if (extra) throw new Error(`${harness} continuation does not carry the companion artifact ${extra.path}`);
|
|
60
|
+
context.signal?.throwIfAborted();
|
|
61
|
+
try { return await canonicalPath(operationPath(cwd, context.cwd)); }
|
|
62
|
+
catch (error) { throw new Error(`continuation cwd is unavailable: ${error.message}`, { cause: error }); }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function* lines(text) {
|
|
66
|
+
if (text === '') return;
|
|
67
|
+
const records = text.split('\n');
|
|
68
|
+
for (let index = 0; index < records.length; index++) {
|
|
69
|
+
if (index === records.length - 1 && records[index] === '') break;
|
|
70
|
+
const line = records[index];
|
|
71
|
+
yield index < records.length - 1 && line.endsWith('\r') ? line.slice(0, -1) : line;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function rewrittenRecords(session, format, id, name, rewrite) {
|
|
75
|
+
let text = '';
|
|
76
|
+
for (const line of lines(toSessionJsonlSpliced(session, format, id))) {
|
|
77
|
+
let value;
|
|
78
|
+
try { value = parseNativeJson(line); }
|
|
79
|
+
catch (error) { throw new Error(`invalid rewritten ${name} record: ${error.message}`, { cause: error }); }
|
|
80
|
+
rewrite(value); text += stringifyNativeJson(value) + '\n';
|
|
81
|
+
}
|
|
82
|
+
return text;
|
|
83
|
+
}
|
|
84
|
+
function loadedSession(artifact, source) {
|
|
85
|
+
const session = readSessionDocument(artifact.content, { source });
|
|
86
|
+
if (artifact.session_id != null && session.meta.session_id !== artifact.session_id) throw new Error('native artifact declared session identity does not match its content');
|
|
87
|
+
return session;
|
|
88
|
+
}
|
|
89
|
+
const fileLocator = (harness, session_id, path) => ({ harness, session_id, storage: { kind: 'file', path } });
|
|
90
|
+
|
|
91
|
+
async function materializeFile(artifact, cwd, homes, context) {
|
|
92
|
+
const { signal } = context, harness = artifact.target_harness;
|
|
93
|
+
const names = { 'claude-code': 'Claude', codex: 'Codex', pi: 'Pi' }, name = names[harness];
|
|
94
|
+
const destination = await validateMaterialization(artifact, cwd, name, [], context);
|
|
95
|
+
const source = harness === 'claude-code' ? 'claude_code' : harness;
|
|
96
|
+
const session = loadedSession(artifact, source), id = freshSessionId();
|
|
97
|
+
const text = rewrittenRecords(session, source, id, name, value => {
|
|
98
|
+
if (!isObject(value)) return;
|
|
99
|
+
if (harness === 'claude-code' || harness === 'pi' && value.type === 'session') value.cwd = destination;
|
|
100
|
+
if (harness === 'codex' && ['session_meta', 'turn_context'].includes(value.type) && isObject(value.payload) && Object.hasOwn(value.payload, 'cwd')) value.payload.cwd = destination;
|
|
101
|
+
});
|
|
102
|
+
const home = operationPath(homes[source], context.cwd);
|
|
103
|
+
let directory, filename;
|
|
104
|
+
if (harness === 'claude-code') {
|
|
105
|
+
const project = [...destination].map(character => /^[A-Za-z0-9]$/u.test(character) ? character : '-').join('');
|
|
106
|
+
directory = await containedDirectory(home, [project], signal, true); filename = `${id}.jsonl`;
|
|
107
|
+
} else if (harness === 'codex') {
|
|
108
|
+
const now = new Date().toISOString();
|
|
109
|
+
directory = await containedDirectory(home, [now.slice(0, 4), now.slice(5, 7), now.slice(8, 10)], signal);
|
|
110
|
+
filename = `rollout-${now.slice(0, 19).replace(/:/gu, '-')}-${id}.jsonl`;
|
|
111
|
+
} else {
|
|
112
|
+
const selected = context.env.PI_CODING_AGENT_SESSION_DIR;
|
|
113
|
+
const flat = selected != null && selected !== '' && await samePath(selected, homes.pi, context.cwd);
|
|
114
|
+
const folder = `--${destination.replace(/^[\/\\]/u, '').replace(/[\/\\:]/gu, '-')}--`;
|
|
115
|
+
directory = await containedDirectory(home, flat ? [] : [folder], signal);
|
|
116
|
+
filename = `${new Date().toISOString().replace(/[:.]/gu, '-')}_${id}.jsonl`;
|
|
117
|
+
}
|
|
118
|
+
const path = join(directory, filename); await writeExclusive(path, text, signal);
|
|
119
|
+
return fileLocator(harness, id, path);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function grokWorkspace(cwd) {
|
|
123
|
+
return [...Buffer.from(cwd)].map(byte => /[A-Za-z0-9._~-]/u.test(String.fromCharCode(byte)) ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, '0')}`).join('');
|
|
124
|
+
}
|
|
125
|
+
async function materializeGrok(artifact, cwd, homes, context) {
|
|
126
|
+
const destination = await validateMaterialization(artifact, cwd, 'Grok', ['summary.json', 'updates.jsonl'], context);
|
|
127
|
+
const session = readSessionDocument(artifact.content, { source: 'grok' });
|
|
128
|
+
const companion = path => artifact.files.find(file => file.role !== 'primary' && file.path === path)?.content;
|
|
129
|
+
const summaryText = companion('summary.json'); let summary;
|
|
130
|
+
try { summary = summaryText == null ? {} : parseNativeJson(summaryText); }
|
|
131
|
+
catch (error) { throw new Error(`invalid Grok summary.json: ${error.message}`, { cause: error }); }
|
|
132
|
+
const sourceId = typeof summary?.info?.id === 'string' ? summary.info.id : null;
|
|
133
|
+
if (artifact.session_id != null && sourceId != null && artifact.session_id !== sourceId) throw new Error('native artifact declared session identity does not match its content');
|
|
134
|
+
const model = typeof summary?.current_model_id === 'string' ? summary.current_model_id : session.meta.model ?? '';
|
|
135
|
+
const id = freshSessionId(), now = new Date().toISOString();
|
|
136
|
+
const transcript = toSessionJsonlSpliced(session, 'grok', id);
|
|
137
|
+
summary = isObject(summary) ? summary : {};
|
|
138
|
+
for (const key of ['agent_id', 'attempt_id', 'request_id', 'grok_home', 'sandbox_profile']) swapRemoveNativeJsonMember(summary, key);
|
|
139
|
+
summary.info = { id, cwd: destination };
|
|
140
|
+
if (!Object.hasOwn(summary, 'session_summary')) summary.session_summary = '';
|
|
141
|
+
if (!Object.hasOwn(summary, 'created_at')) summary.created_at = now;
|
|
142
|
+
summary.updated_at = now;
|
|
143
|
+
if (!Object.hasOwn(summary, 'num_messages')) summary.num_messages = session.messages.length;
|
|
144
|
+
summary.current_model_id = model;
|
|
145
|
+
const directory = await containedDirectory(operationPath(homes.grok, context.cwd), [grokWorkspace(destination), id], context.signal);
|
|
146
|
+
const path = join(directory, 'chat_history.jsonl');
|
|
147
|
+
await writeExclusive(path, transcript, context.signal);
|
|
148
|
+
await writeExclusive(join(directory, 'summary.json'), stringifyNativeJsonPretty(summary), context.signal);
|
|
149
|
+
const updates = companion('updates.jsonl');
|
|
150
|
+
if (updates != null && sourceId != null) {
|
|
151
|
+
let rewritten = '';
|
|
152
|
+
for (const line of lines(updates)) {
|
|
153
|
+
if (nativeTrim(line) === '') continue;
|
|
154
|
+
let record;
|
|
155
|
+
try { record = parseNativeJson(line); }
|
|
156
|
+
catch (error) { throw new Error(`invalid Grok updates record: ${error.message}`, { cause: error }); }
|
|
157
|
+
if (isObject(record?.params)) {
|
|
158
|
+
if (record.params.sessionId === sourceId) record.params.sessionId = id;
|
|
159
|
+
if (isObject(record.params._meta) && typeof record.params._meta.eventId === 'string' && record.params._meta.eventId.startsWith(sourceId)) record.params._meta.eventId = id + record.params._meta.eventId.slice(sourceId.length);
|
|
160
|
+
}
|
|
161
|
+
rewritten += stringifyNativeJson(record) + '\n';
|
|
162
|
+
}
|
|
163
|
+
await writeExclusive(join(directory, 'updates.jsonl'), rewritten, context.signal);
|
|
164
|
+
}
|
|
165
|
+
return fileLocator('grok', id, path);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function materializeSession(params, { cwd = process.cwd(), env = process.env, signal } = {}) {
|
|
169
|
+
signal?.throwIfAborted();
|
|
170
|
+
const context = { cwd, env, signal }, parsed = parseSessionMaterializeParams(params, context);
|
|
171
|
+
const store = createResidueStore(context);
|
|
172
|
+
for (const file of parsed.artifact.files) {
|
|
173
|
+
if (file.role !== 'source_recovery' || file.path !== 'recovery/source.supercode.jsonl') continue;
|
|
174
|
+
let source; try { source = fromNativeStr(file.content); } catch { continue; }
|
|
175
|
+
await store.store(source);
|
|
176
|
+
}
|
|
177
|
+
signal?.throwIfAborted();
|
|
178
|
+
const harness = parsed.artifact.target_harness;
|
|
179
|
+
if (['claude-code', 'codex', 'pi'].includes(harness)) return { locator: await materializeFile(parsed.artifact, parsed.cwd, parsed.homes, context) };
|
|
180
|
+
if (harness === 'grok') return { locator: await materializeGrok(parsed.artifact, parsed.cwd, parsed.homes, context) };
|
|
181
|
+
if (harness === 'opencode') {
|
|
182
|
+
const destination = await validateMaterialization(parsed.artifact, parsed.cwd, 'OpenCode', [], context);
|
|
183
|
+
return { locator: await materializeOpenCode(parsed.artifact, destination, parsed.homes.opencode, context) };
|
|
184
|
+
}
|
|
185
|
+
throw new Error('native materialization is not yet supported for this harness');
|
|
186
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Public message admission and the user-level Claude inbound report. This
|
|
2
|
+
// reads settings only; delivery and policy changes retain their own owners.
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, parse, sep } from 'node:path';
|
|
5
|
+
import { blake3 } from '@noble/hashes/blake3.js';
|
|
6
|
+
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
7
|
+
import { resolveHarnessHomes } from './homes.mjs';
|
|
8
|
+
import { parseNativeJson, stringifyNativeJsonMember } from './native-json.mjs';
|
|
9
|
+
import { treeSelectedPath } from './session-machine-tree.mjs';
|
|
10
|
+
|
|
11
|
+
const invalid = message => { throw Object.assign(new Error(message), { rpcCode: -32602 }); };
|
|
12
|
+
const fields = ['locator', 'text', 'subject', 'idempotency_key', 'channel', 'from_name', 'voice_for', 'sender_name', 'in_reply_to', 'marker', 'reply_marker', 'surface', 'notify_when_idle', 'as_user', 'homes'];
|
|
13
|
+
function struct(value, keys, name) {
|
|
14
|
+
if (value == null || typeof value !== 'object') invalid(`${name} must be a struct`);
|
|
15
|
+
if (!Array.isArray(value)) return value;
|
|
16
|
+
if (value.length > keys.length) invalid(`too many ${name} fields`);
|
|
17
|
+
return Object.fromEntries(value.map((item, at) => [keys[at], item]));
|
|
18
|
+
}
|
|
19
|
+
const string = (value, name) => { if (typeof value !== 'string') invalid(`${name} must be a string`); };
|
|
20
|
+
// Rust char::is_whitespace follows Unicode White_Space, which differs from
|
|
21
|
+
// JavaScript's \s for U+0085 and U+FEFF.
|
|
22
|
+
const whitespace = /[\u0009-\u000d\u0020\u0085\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]/u;
|
|
23
|
+
function address(value) {
|
|
24
|
+
string(value, 'voice_for');
|
|
25
|
+
if (!/^sc:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+:.+$/su.test(value) || whitespace.test(value)) invalid('voice_for must be a session address');
|
|
26
|
+
}
|
|
27
|
+
export function parseSessionMessageParams(params, { env = process.env } = {}) {
|
|
28
|
+
const decoded = struct(params, fields, 'message params');
|
|
29
|
+
const target = struct(decoded.locator, ['harness', 'session_id', 'storage'], 'locator');
|
|
30
|
+
string(target.harness, 'locator.harness'); string(target.session_id, 'locator.session_id'); string(decoded.text, 'text');
|
|
31
|
+
if (target.storage != null) {
|
|
32
|
+
if (typeof target.storage !== 'object' || Array.isArray(target.storage)) invalid('locator.storage must be an object');
|
|
33
|
+
if (!['file', 'sqlite'].includes(target.storage.kind)) invalid('locator.storage.kind must be file or sqlite');
|
|
34
|
+
string(target.storage.path, 'locator.storage.path');
|
|
35
|
+
if (target.storage.kind === 'sqlite') string(target.storage.selector, 'locator.storage.selector');
|
|
36
|
+
}
|
|
37
|
+
for (const key of ['subject', 'idempotency_key', 'from_name', 'sender_name', 'in_reply_to', 'marker', 'reply_marker', 'surface']) if (decoded[key] != null) string(decoded[key], key);
|
|
38
|
+
if (decoded.voice_for != null) address(decoded.voice_for);
|
|
39
|
+
for (const key of ['channel', 'notify_when_idle', 'as_user']) if (Object.hasOwn(decoded, key) && typeof decoded[key] !== 'boolean') invalid(`${key} must be a boolean`);
|
|
40
|
+
const homes = resolveHarnessHomes(env);
|
|
41
|
+
if (Object.hasOwn(decoded, 'homes')) {
|
|
42
|
+
const supplied = struct(decoded.homes, Object.keys(homes), 'homes');
|
|
43
|
+
for (const key of Object.keys(homes)) if (Object.hasOwn(supplied, key)) { string(supplied[key], `homes.${key}`); homes[key] = supplied[key]; }
|
|
44
|
+
}
|
|
45
|
+
return { homes };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function settingsPath(root) {
|
|
49
|
+
let parent;
|
|
50
|
+
if (!root || root === parse(root).root) parent = '.';
|
|
51
|
+
else { parent = dirname(root); if (parent === '.' && !root.includes(sep) && !(process.platform === 'win32' && root.includes('/'))) parent = ''; }
|
|
52
|
+
return !parent ? 'settings.json' : `${parent}${parent.endsWith(sep) || process.platform === 'win32' && /^[A-Za-z]:$/.test(parent) ? '' : sep}settings.json`;
|
|
53
|
+
}
|
|
54
|
+
export function readClaudeInteropSettings(homes, cwd) {
|
|
55
|
+
const path = settingsPath(homes.claude_code); let bytes;
|
|
56
|
+
try { bytes = readFileSync(treeSelectedPath(path, cwd)); }
|
|
57
|
+
catch (error) { if (error.code === 'ENOENT') bytes = new Uint8Array(); else throw new Error(`Claude Code settings I/O failed: ${error.message}`); }
|
|
58
|
+
let settings;
|
|
59
|
+
try { settings = bytes.length ? parseNativeJson(new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes)) : {}; }
|
|
60
|
+
catch (error) { throw new Error(`Claude Code settings JSON is invalid: ${error.message}`); }
|
|
61
|
+
if (settings == null || typeof settings !== 'object' || Array.isArray(settings)) throw new Error(`Claude Code settings at ${path} must be a JSON object`);
|
|
62
|
+
const configured = Object.hasOwn(settings, 'crossSessionInbound') ? settings.crossSessionInbound : null;
|
|
63
|
+
if (Object.hasOwn(settings, 'crossSessionInbound') && !['accept', 'hold', 'refuse'].includes(configured)) throw new Error(`Claude Code setting \`crossSessionInbound\` at ${path} must be \`accept\`, \`hold\`, or \`refuse\`, not ${stringifyNativeJsonMember(settings, 'crossSessionInbound')}`);
|
|
64
|
+
return {
|
|
65
|
+
schema: 'supercode.harness-interop-settings.v1', harness: 'claude-code', revision: bytesToHex(blake3(bytes)),
|
|
66
|
+
controls: [{ key: 'cross_session_inbound', native_key: 'crossSessionInbound', label: 'Messages from other sessions',
|
|
67
|
+
description: 'How Claude Code handles messages arriving from another live Claude Code session.', scope: 'user', source_path: path,
|
|
68
|
+
configured_value: configured, effective_value: null, effective_known: false,
|
|
69
|
+
effective_note: 'This is the user-level value. Managed, project, or command-line policy may override it for a particular process.',
|
|
70
|
+
choices: [{ value: 'accept', label: 'Allow automatically', description: "Deliver messages from the user's other Claude Code sessions without a separate inbound approval.",
|
|
71
|
+
risk: "A trusted peer session can introduce instructions into this session; the receiver's configured permission mode still governs subsequent tool use." },
|
|
72
|
+
{ value: 'hold', label: 'Ask before delivery', description: 'Hold messages from sessions in a different permission-mode class for review.' },
|
|
73
|
+
{ value: 'refuse', label: 'Refuse automatically', description: 'Do not deliver messages from sessions in a different permission-mode class.' }],
|
|
74
|
+
writable: true, resettable: configured != null, requires_restart: false }],
|
|
75
|
+
advisories: configured === 'accept' ? [] : [{ code: 'claude_cross_session_inbound_accept', severity: 'warning', title: 'Claude may hold messages from other sessions',
|
|
76
|
+
message: "Volter Harness can hand a message to Claude's inbox, but Claude may hold or refuse it under the current user-level inbound policy.", setting: 'cross_session_inbound',
|
|
77
|
+
recommendation: { label: 'Allow messages from my other Claude sessions', description: "Set Claude Code's user-level cross-session inbound policy to accept.",
|
|
78
|
+
consequence: "Other Claude Code sessions owned by this user can introduce instructions without a separate inbound approval. The receiving session's configured permission mode still applies.",
|
|
79
|
+
change: { key: 'cross_session_inbound', value: 'accept' }, command: 'supercode harness configure claude-code --cross-session-inbound accept' } }],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function sessionMessageInboundControls(homes, { cwd = process.cwd() } = {}) {
|
|
83
|
+
try { return { inbound_controls: readClaudeInteropSettings(homes, cwd), inbound_controls_error: null }; }
|
|
84
|
+
catch (error) { return { inbound_controls: null, inbound_controls_error: `Volter Harness could not inspect Claude Code inbound controls: ${error.message}` }; }
|
|
85
|
+
}
|
package/session-model.d.ts
CHANGED
|
@@ -21,7 +21,12 @@ export interface MessageWire {
|
|
|
21
21
|
}
|
|
22
22
|
export interface MessageJson extends MessageWire { metadata: Record<string, string> }
|
|
23
23
|
export type ToolOutcome = 'known_success' | 'known_error' | 'unknown';
|
|
24
|
+
export interface NativeFirstResponse {
|
|
25
|
+
outcome: 'response' | 'error'; at: string | null; record_id: string | null; error_code: string | null;
|
|
26
|
+
}
|
|
24
27
|
export interface SessionMeta {
|
|
28
|
+
/** First main-chain Claude assistant record, including native API errors. */
|
|
29
|
+
first_response?: NativeFirstResponse;
|
|
25
30
|
source: SessionSource; session_id: string | null; ended_at: string | null; end_reason: string | null;
|
|
26
31
|
model: string | null; cwd: string | null; system_prompt: string | null;
|
|
27
32
|
codex_headers: JsonValue[]; codex_provenance: JsonValue[];
|
|
@@ -39,6 +44,8 @@ export interface Session {
|
|
|
39
44
|
raw_is_verbatim: boolean; parse_error_lines: number; load_residue: string[];
|
|
40
45
|
}
|
|
41
46
|
export interface NormalizedSession {
|
|
47
|
+
/** First main-chain Claude assistant record; response does not attest turn completion. */
|
|
48
|
+
first_response?: NativeFirstResponse;
|
|
42
49
|
source: SessionSource; session_id: string | null; ended_at: string | null; end_reason: string | null;
|
|
43
50
|
model: string | null; cwd: string | null; system_prompt: string | null;
|
|
44
51
|
agent_id: string | null; parent_tool_use_id: string | null; lineage: Record<string, string>;
|
|
@@ -60,6 +67,8 @@ export interface RecordedSessionProfile {
|
|
|
60
67
|
}
|
|
61
68
|
export interface RecordedSessionProfileChange { native_key: string; at: string | null; profile: RecordedSessionProfile }
|
|
62
69
|
export interface ProjectedSession {
|
|
70
|
+
/** First main-chain Claude assistant record; response does not attest turn completion. */
|
|
71
|
+
first_response?: NativeFirstResponse;
|
|
63
72
|
source: SessionSource; session_id: string | null; ended_at: string | null; end_reason: string | null;
|
|
64
73
|
model: string | null; resolved_profile: RecordedSessionProfile; resolved_profile_history: RecordedSessionProfileChange[];
|
|
65
74
|
cwd: string | null; system_prompt: string | null; agent_id: string | null; parent_tool_use_id: string | null;
|
package/session-model.mjs
CHANGED
|
@@ -50,6 +50,7 @@ export function normalizedSessionJson(session) {
|
|
|
50
50
|
const { meta } = session;
|
|
51
51
|
return {
|
|
52
52
|
source: meta.source, session_id: meta.session_id, ended_at: meta.ended_at, end_reason: meta.end_reason,
|
|
53
|
+
...(meta.first_response ? { first_response: cloneNativeJson(meta.first_response) } : {}),
|
|
53
54
|
model: meta.model, cwd: meta.cwd, system_prompt: meta.system_prompt, agent_id: meta.agent_id,
|
|
54
55
|
parent_tool_use_id: meta.parent_tool_use_id, lineage: cloneNativeJson(meta.lineage), trigger: sessionTrigger(meta),
|
|
55
56
|
surface: cloneNativeJson(meta.surface), profile: meta.profile, recurrence: cloneNativeJson(meta.recurrence),
|
|
@@ -3,7 +3,7 @@ import { readStore, statement, tableColumns, text, integer } from './sqlite-read
|
|
|
3
3
|
import { sourceStat } from './store-registry.mjs';
|
|
4
4
|
import { optionalFile, verifySources } from './source-files.mjs';
|
|
5
5
|
import { sessionFileText } from './session-file.mjs';
|
|
6
|
-
import { parseNativeJson, setNativeInteger, setNativeFloat } from './native-json.mjs';
|
|
6
|
+
import { parseNativeJson, copyNativeJsonMember, setNativeInteger, setNativeFloat } from './native-json.mjs';
|
|
7
7
|
import { isObject, stringField } from './session-codec-utils.mjs';
|
|
8
8
|
import { fromOpenCodeStr, opencodeEnvelope } from './session-opencode.mjs';
|
|
9
9
|
|
|
@@ -51,8 +51,11 @@ function nativeRow(row, sessionId, messageId, options) {
|
|
|
51
51
|
const id = text(row.id), value = jsonColumn(text(row.data), messageId == null ? 'message.data' : 'part.data', id, options);
|
|
52
52
|
// A malformed/non-object data column remains diagnostically absent, as native.
|
|
53
53
|
if (isObject(value)) {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Register injected identity fields before timestamps in the parsed
|
|
55
|
+
// object's native insertion order, matching the SQLite envelope writer.
|
|
56
|
+
const identity = { id, sessionID: sessionId };
|
|
57
|
+
if (messageId != null) identity.messageID = messageId;
|
|
58
|
+
for (const key of Object.keys(identity)) copyNativeJsonMember(value, key, identity);
|
|
56
59
|
stamp(value, 'time_created', integer(row.time_created)); stamp(value, 'time_updated', integer(row.time_updated));
|
|
57
60
|
} else { integer(row.time_created); integer(row.time_updated); }
|
|
58
61
|
return value;
|
|
@@ -99,3 +102,36 @@ export async function readOpenCodeSqlite(path, sessionId = null, options = {}) {
|
|
|
99
102
|
const session = fromOpenCodeStr(`${lines.join('\n')}\n`); session.raw_is_verbatim = false;
|
|
100
103
|
return { result: session, sources };
|
|
101
104
|
}
|
|
105
|
+
|
|
106
|
+
/** Complete corpus projection using the same native row reconstruction as
|
|
107
|
+
* selected-session reads. A zero native limit still selects the first id. */
|
|
108
|
+
export async function readOpenCodeCorpus(path, limit = null, options = {}) {
|
|
109
|
+
if (!(await sourceStat(path))?.isFile()) throw new Error(`OpenCode SQLite store not found at ${path} — expected an \`opencode*.db\` file`);
|
|
110
|
+
const captured = await readStore(path, db => {
|
|
111
|
+
if (!statement(db, "SELECT count(*) AS count FROM sqlite_master WHERE type='table' AND name='session'").get().count) throw new Error(`${path} is a SQLite database but has no \`session\` table — not a recognized OpenCode store`);
|
|
112
|
+
const selected = [];
|
|
113
|
+
for (const row of statement(db, 'SELECT id FROM session ORDER BY time_created,id').iterate()) {
|
|
114
|
+
const id = text(row.id); selected.push({id, lines:envelopeLines(db, id, options)});
|
|
115
|
+
if (limit != null && BigInt(selected.length) >= limit) break;
|
|
116
|
+
}
|
|
117
|
+
return selected;
|
|
118
|
+
}, options.metrics);
|
|
119
|
+
const lines = [], sources = [...captured.sources];
|
|
120
|
+
for (const selected of captured.result) {
|
|
121
|
+
lines.push(...selected.lines);
|
|
122
|
+
const sidecarPath = join(dirname(path), 'storage', 'session_diff', `${selected.id}.json`);
|
|
123
|
+
// The corpus sidecar is best effort, including read/UTF-8 failures.
|
|
124
|
+
// A present malformed JSON value still emits the native diagnostic.
|
|
125
|
+
try {
|
|
126
|
+
const sidecar = await optionalFile(sidecarPath, async reader => {
|
|
127
|
+
const source = await sessionFileText(reader);
|
|
128
|
+
try { return {parsed:true, value:parseNativeJson(source)}; }
|
|
129
|
+
catch (error) { diagnostic(options, {message:'opencode session_diff sidecar failed to parse as JSON — skipping', path:sidecarPath, error:error.message}); return {parsed:false}; }
|
|
130
|
+
}, options.metrics);
|
|
131
|
+
sources.push(sidecar);
|
|
132
|
+
if (sidecar.result?.parsed) lines.push(opencodeEnvelope(['session_diff', selected.id], sidecar.result.value));
|
|
133
|
+
} catch (error) { if (error.code === 'ESTALE') throw error; }
|
|
134
|
+
}
|
|
135
|
+
await verifySources(sources);
|
|
136
|
+
return lines;
|
|
137
|
+
}
|
package/session-path.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { withSessionFile, fileFingerprint } from './file-io.mjs';
|
|
|
6
6
|
import { sourceStat } from './store-registry.mjs';
|
|
7
7
|
import { optionalFile, verifySources, decodeWorkspaceFolder } from './source-files.mjs';
|
|
8
8
|
import { readStore, statement } from './sqlite-reader.mjs';
|
|
9
|
-
import { decodeSessionUtf8, sessionFileBytes, sessionFileText, textTooLong, SESSION_UNREADABLE } from './session-file.mjs';
|
|
9
|
+
import { decodeSessionUtf8, sessionFileBytes, sessionFileText, textTooLong, sessionByteLimit, MAX_SESSION_DOCUMENT_BYTES, SESSION_UNREADABLE } from './session-file.mjs';
|
|
10
10
|
import { field, stringField, valueString } from './session-codec-utils.mjs';
|
|
11
11
|
import { parseNativeJson } from './native-json.mjs';
|
|
12
12
|
import { nonEmptyLines } from './session-lines.mjs';
|
|
@@ -26,8 +26,11 @@ export async function sessionReaderText(reader) {
|
|
|
26
26
|
let bytes = await sessionFileBytes(reader);
|
|
27
27
|
if (isCompressedSessionPath(reader.path)) {
|
|
28
28
|
if (typeof zlib.zstdDecompress !== 'function') throw new Error('compressed transcripts require Node 22.15 or later with zstdDecompress');
|
|
29
|
-
try { bytes = await promisify(zlib.zstdDecompress)(bytes); }
|
|
30
|
-
catch (error) {
|
|
29
|
+
try { bytes = await promisify(zlib.zstdDecompress)(bytes, { maxOutputLength: MAX_SESSION_DOCUMENT_BYTES }); }
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (error.code === 'ERR_BUFFER_TOO_LARGE') throw sessionByteLimit(reader.path, 'decompressed output', error);
|
|
32
|
+
throw new Error(`${reader.path} is not a readable zstd stream: ${error.message}`, { cause: error });
|
|
33
|
+
}
|
|
31
34
|
}
|
|
32
35
|
try { return decodeSessionUtf8(bytes); } catch (error) { throw sessionTextError(reader.path, error); }
|
|
33
36
|
}
|
package/session-pi-content.mjs
CHANGED
|
@@ -4,6 +4,10 @@ import { field, stringField, valueString, functionCall } from './session-codec-u
|
|
|
4
4
|
import { nativeTrim } from './text.mjs';
|
|
5
5
|
|
|
6
6
|
const append = (text, part) => typeof part === 'string' ? text + (text === '' ? '' : '\n') + part : text;
|
|
7
|
+
export function piImageShape(block) {
|
|
8
|
+
const mime = stringField(block, 'mimeType'), data = stringField(block, 'data');
|
|
9
|
+
return mime != null && mime !== '' && data != null && data !== '' && !/[^A-Za-z0-9+/=]/.test(data) ? {mime, data} : null;
|
|
10
|
+
}
|
|
7
11
|
function stampNativeClock(value, message) {
|
|
8
12
|
const timestamp = nativeInteger(value, 'timestamp');
|
|
9
13
|
if (timestamp != null) message.metadata.pi_msg_timestamp = String(timestamp);
|
|
@@ -22,8 +26,9 @@ function contentParts(content) {
|
|
|
22
26
|
if (kind === 'text') text = append(text, stringField(block, 'text'));
|
|
23
27
|
else if (kind === 'image') {
|
|
24
28
|
hasImage = true;
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
29
|
+
const shape = piImageShape(block);
|
|
30
|
+
if (shape == null) return { text: '', parts: null, unknown: true };
|
|
31
|
+
const {mime, data} = shape;
|
|
27
32
|
parts.push({ type: 'image_url', image_url: { url: `data:${mime};base64,${data}` } });
|
|
28
33
|
}
|
|
29
34
|
}
|
package/session-read.d.ts
CHANGED
|
@@ -5,13 +5,53 @@ export const SESSION_READ_METHODS: readonly string[];
|
|
|
5
5
|
export const SESSION_READ_EVENT: 'harness.v1.sessions.event';
|
|
6
6
|
/** The read doors' error code for a file this reader cannot read for what is in it. */
|
|
7
7
|
export const SESSION_UNREADABLE: -32050;
|
|
8
|
-
export const SESSION_READ_CAPABILITY: Readonly<{ version: 1; owner: 'caller'; language: 'typescript'; native_parser: false; recorded_config: true; launch_agent: true; usage: true; spend: true; resume_instructions: true }>;
|
|
8
|
+
export const SESSION_READ_CAPABILITY: Readonly<{ version: 1; owner: 'caller'; language: 'typescript'; native_parser: false; recorded_config: true; launch_agent: true; usage: true; spend: true; resume_instructions: true; tree: true; activity_under: true; activity: true; inbox: true; message: true; import: true; export: true; translate: true; branch: true; handoff: true; materialize: true; support: true; settings: true; skills: true; skills_control: true; profiles: true; profiles_control: true; memory_show: true; job_notepad: true; runs: true; jobs: true; jobs_control: true; routes: true; triggers: true; channels: true; workflow: true }>;
|
|
9
|
+
export const SESSION_READ_OPERATIONS: readonly ['export', 'skills_list', 'skills_install', 'skills_remove', 'profiles_list', 'profiles_get', 'profiles_create', 'profiles_delete', 'jobs_notepad', 'runs_list', 'runs_get', 'jobs_list', 'jobs_get', 'jobs_create', 'jobs_update', 'jobs_pause', 'jobs_resume', 'jobs_run', 'jobs_delete', 'jobs_notepad_set', 'jobs_notepad_delete', 'routes_list', 'triggers_list', 'channels_list', 'channels_status', 'workflow_load'];
|
|
9
10
|
export interface SessionReadContext { cwd?: string; env?: Record<string, string | undefined> }
|
|
11
|
+
export interface ProfileRow {
|
|
12
|
+
name: string; harness: string;
|
|
13
|
+
kind: 'preset' | 'codex_profile' | 'hermes_profile' | 'openclaw_agent' | 'orchestrator_profile';
|
|
14
|
+
home: string | null; default: boolean; routes: number | null; sessions: number | null; model: string | null; worker?: string;
|
|
15
|
+
}
|
|
16
|
+
/** The harness command is followed by its own profile-store readback. */
|
|
17
|
+
export function mutateProfile(verb: 'create' | 'delete', params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<{ harness: 'hermes' | 'openclaw' | 'orchestrator'; verb: 'create' | 'delete'; ran: string; name: string; profile?: ProfileRow; deleted?: boolean }>;
|
|
18
|
+
/** Counts preserve native integer literals on the RPC wire. */
|
|
19
|
+
export function listProfiles(params: unknown, context?: SessionReadContext): { schema: 'supercode.profiles.v1'; profiles: ProfileRow[] };
|
|
20
|
+
export function getProfile(params: unknown, context?: SessionReadContext): { schema: 'supercode.profiles.v1'; profile: ProfileRow };
|
|
21
|
+
export interface RecordedChannel {
|
|
22
|
+
name: string; harness: string; kind: string; account: string | null; enabled: boolean | null; configured: boolean; status: 'unknown'; sessions: number | null;
|
|
23
|
+
}
|
|
24
|
+
/** Credential presence and catalog session counts only; probes no connection. */
|
|
25
|
+
export function listChannels(params: unknown, context?: SessionReadContext): Promise<{ schema: 'supercode.channels.v1'; channels: RecordedChannel[] }>;
|
|
26
|
+
export function channelStatus(params: unknown, context?: SessionReadContext): Promise<{ schema: 'supercode.channels.v1'; channel: RecordedChannel }>;
|
|
27
|
+
export interface RecordedTrigger {
|
|
28
|
+
name: string; harness: string; kind: 'webhook' | 'hook_mapping' | 'builtin_wake' | 'builtin_agent'; route: string; events?: string[];
|
|
29
|
+
target: { action?: string; profile?: string; session_key?: string; wake_mode?: string; model?: string };
|
|
30
|
+
deliver: { target?: string; chat_id?: string }; enabled: boolean; authenticated: boolean; source: string; description?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Recorded endpoint facts only, after complete home validity; starts no trigger. */
|
|
33
|
+
export function listTriggers(params: unknown, context?: SessionReadContext): { schema: 'supercode.triggers.v1'; triggers: RecordedTrigger[] };
|
|
34
|
+
export interface RecordedRoute {
|
|
35
|
+
harness: string; target: string;
|
|
36
|
+
match: { platform?: string; account?: string; guild?: string; team?: string; chat_id?: string; peer_kind?: string; thread_id?: string; roles?: string[] };
|
|
37
|
+
specificity: number; default: boolean; source: string;
|
|
38
|
+
}
|
|
39
|
+
/** Recorded matches and precedence after complete home validity; resolves no traffic. */
|
|
40
|
+
export function listRoutes(params: unknown, context?: SessionReadContext): { schema: 'supercode.routes.v1'; routes: RecordedRoute[] };
|
|
41
|
+
export interface MemoryDocument {
|
|
42
|
+
name: string; harness: string; scope: 'user' | 'project' | 'profile' | 'agent';
|
|
43
|
+
profile?: string; path: string; size: number; updated_at?: string;
|
|
44
|
+
preview: string[]; truncated: boolean; content?: string;
|
|
45
|
+
}
|
|
46
|
+
/** Native integer sizes are retained on the RPC wire. */
|
|
47
|
+
export function showMemory(params: unknown, context?: SessionReadContext): { schema: 'supercode.memory.v1'; documents: MemoryDocument[] };
|
|
10
48
|
export function parseSessionWire(text: string): unknown;
|
|
11
49
|
export function stringifySessionWire(value: unknown): string;
|
|
50
|
+
/** Keeps the original request ID's native integer/float facts on its reply. */
|
|
51
|
+
export function sessionWireReply(request: { id?: unknown } | null | undefined, answer: Record<string, unknown>): Record<string, unknown>;
|
|
12
52
|
export function prettySessionWire(value: unknown): string;
|
|
13
53
|
export function parseSessionReadParams(params: unknown, context?: SessionReadContext): { locator: ReadLocator; fidelity: string; view: (SessionFollowOptions & { fromByte: number | null; fromMessage: number | null; windowBytes: number | null; windowMessages: number | null; marks: boolean }) | null; options: import('./session-model.js').SessionLoadOptions | null };
|
|
14
|
-
export function loadSessionRead(params: unknown, context?: SessionReadContext): Promise<ProjectedSessionResult | { session: NormalizedSession & { resolved_profile: RecordedSessionProfile; resolved_profile_history: RecordedSessionProfileChange[]; window?: { offset: number; returned: number; total_messages: number; marks: string[];
|
|
54
|
+
export function loadSessionRead(params: unknown, context?: SessionReadContext): Promise<ProjectedSessionResult | { session: NormalizedSession & { resolved_profile: RecordedSessionProfile; resolved_profile_history: RecordedSessionProfileChange[]; window?: { offset: number; returned: number; total_messages: number | null; marks: string[]; marks_from?: number; tail_marks?: string[]; reading?: { bytes: number; size: number }; replay?: { whole: boolean; reason: string | null } } } }>;
|
|
15
55
|
export function openSessionReadFollower(params: unknown, context?: SessionReadContext): Promise<SessionFollower>;
|
|
16
56
|
/** Recorded values only; malformed complete Claude runtime state leaves mode unknown. */
|
|
17
57
|
export function loadRecordedSessionConfiguration(params: unknown, context?: SessionReadContext): Promise<{ harness: string; session_id: string | null; cwd: unknown; model: unknown; approval_policy: unknown; sandbox_policy: unknown; permission_mode: string | null }>;
|
|
@@ -36,3 +76,95 @@ export interface MachineTokenSpend extends RecordedTokenSpend {
|
|
|
36
76
|
export function loadSessionSpend(params: unknown, context?: SessionReadContext): Promise<{ since: string; until: string; session_count: number; sessions: MachineTokenSpend[]; total: RecordedTokenSpend }>;
|
|
37
77
|
/** Reads the complete source and returns a specification; starts no process. */
|
|
38
78
|
export function loadSessionResumeInstructions(params: unknown, context?: SessionReadContext): Promise<{ launch: { cwd: string; program: string; arguments: string[]; env: Record<string, string> } }>;
|
|
79
|
+
|
|
80
|
+
/** Strict native integer facts for opaque daemon replies, independent of SDK Number decoding. */
|
|
81
|
+
export function sessionWireInteger(parent: unknown, key: string, options?: { signed?: boolean }): bigint | null;
|
|
82
|
+
export function parseSessionMessageParams(params: unknown, context?: SessionReadContext): { homes: Record<string, string> };
|
|
83
|
+
/** User-level evidence only; it neither changes policy nor establishes an effective process value. */
|
|
84
|
+
export function sessionMessageInboundControls(homes: Record<string, string>, context?: SessionReadContext): { inbound_controls: unknown; inbound_controls_error: string | null };
|
|
85
|
+
|
|
86
|
+
/** Explicit in-memory document import; no store, vendor process or format detection. */
|
|
87
|
+
export function importSessionDocument(params: unknown): { session: NormalizedSession };
|
|
88
|
+
|
|
89
|
+
/** Compiled implementation facts; does not start or probe a vendor. */
|
|
90
|
+
export function supportRegistryReport(context?: SessionReadContext): { schema: "supercode.support-registry.v1"; harnesses: Record<string, unknown>[] };
|
|
91
|
+
|
|
92
|
+
/** User-level settings only; no mutation or effective-process-policy claim. */
|
|
93
|
+
export function loadHarnessSettings(params: unknown, context?: SessionReadContext): {
|
|
94
|
+
schema: 'supercode.harness-interop-settings.v1'; harness: string; revision: string;
|
|
95
|
+
controls: Array<{
|
|
96
|
+
key: string; native_key: string; label: string; description: string; scope: string;
|
|
97
|
+
source_path: string; configured_value: string | null; effective_value: string | null;
|
|
98
|
+
effective_known: boolean; effective_note: string;
|
|
99
|
+
choices: Array<{ value: string; label: string; description: string; risk?: string }>;
|
|
100
|
+
writable: boolean; resettable: boolean; requires_restart: boolean;
|
|
101
|
+
}>;
|
|
102
|
+
advisories: Array<{
|
|
103
|
+
code: string; severity: string; title: string; message: string; setting: string;
|
|
104
|
+
recommendation: { label: string; description: string; consequence: string;
|
|
105
|
+
change: { key: string; value: string | null }; command: string };
|
|
106
|
+
}>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** Read-only installed packages; runtime loading retains its native owner. */
|
|
110
|
+
export function listInstalledSkills(params: unknown, context?: SessionReadContext): Array<{
|
|
111
|
+
name: string; harness: string; scope: 'managed' | 'user' | 'project' | 'plugin' | 'bundled';
|
|
112
|
+
location: string; description?: string; version?: string; enabled: boolean | null;
|
|
113
|
+
}>;
|
|
114
|
+
|
|
115
|
+
/** Install/remove through the selected harness's CLI or documented loader root. */
|
|
116
|
+
export function mutateInstalledSkill(verb: 'install' | 'remove', params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<{
|
|
117
|
+
harness: string; verb: 'install' | 'remove'; ran: string; name: string;
|
|
118
|
+
skill?: ReturnType<typeof listInstalledSkills>[number]; removed?: true;
|
|
119
|
+
}>;
|
|
120
|
+
|
|
121
|
+
/** Read-only Hermes notepad; selected job/profile must exist before opening its store. */
|
|
122
|
+
export function readJobNotepad(params: unknown, context?: { env?: Record<string, string | undefined>; cwd?: string }): { harness: string; id: string; entries: { key: string; value: string; updated_at: string }[] };
|
|
123
|
+
|
|
124
|
+
/** Complete validated home history; read-only, with exact native integer facts on the wire. */
|
|
125
|
+
export interface RecordedRunDelivery { target: string | null; state: string | null; attempts: number | null; last_error: string | null; delivered_at: string | null }
|
|
126
|
+
export interface RecordedRun { id: string; harness: string; job_id: string; status: string; claimed_at: string | null; started_at: string | null; finished_at: string | null; error: string | null; session_id: string | null; delivery: RecordedRunDelivery | null }
|
|
127
|
+
export interface RecordedRunSource { harness: string; path: string; state: 'read' | 'absent_store' | 'unreadable'; profile: string | null; detail: string | null }
|
|
128
|
+
export function listRuns(params: unknown, context?: SessionReadContext): { runs: RecordedRun[]; sources: RecordedRunSource[] };
|
|
129
|
+
export function getRun(params: unknown, context?: SessionReadContext): { run: RecordedRun; source: Record<string, unknown> };
|
|
130
|
+
|
|
131
|
+
/** Complete job sources and observed projections; starts no scheduler. */
|
|
132
|
+
export interface RecordedJob {
|
|
133
|
+
id: string; harness: string; scope: 'session' | 'install'; profile: string | null; session_id: string | null;
|
|
134
|
+
schedule: { kind: string; expr: string | null; minutes: number | null; run_at: string | null; display: string };
|
|
135
|
+
payload: { kind: string; text: string | null }; session_target: string | null;
|
|
136
|
+
deliver: { target: string | null; chat_id: string | null; thread_id: string | null; account: string | null; mode: string | null };
|
|
137
|
+
enabled: boolean; state: string; next_run_at: string | null; last_run_at: string | null; last_status: string | null;
|
|
138
|
+
last_delivery_error: string | null; created_at: string | null; recurring: boolean;
|
|
139
|
+
}
|
|
140
|
+
export interface RecordedJobSource { harness: string; path: string; state: 'read' | 'absent_store' | 'unreadable' | 'scanned'; profile: string | null; sessions_scanned: number | null; scan_limit: number | null; detail: string | null }
|
|
141
|
+
export function listJobs(params: unknown, context?: SessionReadContext): Promise<{ jobs: RecordedJob[]; sources: RecordedJobSource[] }>;
|
|
142
|
+
export function getJob(params: unknown, context?: SessionReadContext): Promise<{ job: RecordedJob; source: unknown }>;
|
|
143
|
+
|
|
144
|
+
/** Complete read-only Hermes or supercode board projection; no dispatch or writer. */
|
|
145
|
+
export function loadWorkflow(params: unknown, context?: SessionReadContext): { workflow: { root: string; boards: Record<string, unknown> } };
|
|
146
|
+
|
|
147
|
+
export interface SessionTransferArtifact {
|
|
148
|
+
source_harness: string; target_harness: string; session_id: string | null;
|
|
149
|
+
content: string; suggested_filename: string;
|
|
150
|
+
files: Array<{ path: string; content: string; role: 'primary' | 'subagent' | 'bundle' | 'source_recovery' }>;
|
|
151
|
+
fidelity: 'byte_lossless' | 'value_lossless' | 'semantic'; residue: string[];
|
|
152
|
+
}
|
|
153
|
+
export interface ImportedHermesSession { session_id: string; home: string; rollout: string; via: 'hermes sessions import --from codex' }
|
|
154
|
+
/** Full lossless load and artifact construction. Export to Hermes invokes its own import command;
|
|
155
|
+
* every transfer may populate the content-addressed source residue cache. */
|
|
156
|
+
export function transferSession(params: unknown, context?: SessionReadContext & { exporting?: boolean; signal?: AbortSignal }): Promise<{ artifact: SessionTransferArtifact; imported?: ImportedHermesSession }>;
|
|
157
|
+
|
|
158
|
+
/** Frozen branch with an optional target artifact. Cuts require one recorded user-message ID. */
|
|
159
|
+
export function branchSession(params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<{ parent: ReadLocator; session: NormalizedSession; bootstrap_prompt: string; artifact: SessionTransferArtifact | null }>;
|
|
160
|
+
export interface HandoffLaunch { cwd: string; program: string; arguments: string[]; env: Record<string, string> }
|
|
161
|
+
/** Fresh target identity and launch/materialization instructions; invokes no vendor command. */
|
|
162
|
+
export function handoffSession(params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<{ artifact: SessionTransferArtifact; launch: HandoffLaunch; materialize: HandoffLaunch | null; requires_materialization: true; note: string }>;
|
|
163
|
+
|
|
164
|
+
/** Fresh-ID exclusive continuation files or OpenCode CLI import/read-only confirmation.
|
|
165
|
+
* Recovery cache writes precede validation; earlier bundle files survive later failure. */
|
|
166
|
+
export function materializeSession(params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<{ locator: ReadLocator }>;
|
|
167
|
+
|
|
168
|
+
export const CONTROLLED_JOB_HARNESSES: readonly ['hermes', 'openclaw', 'orchestrator'];
|
|
169
|
+
export function mutateJob(verb: 'create' | 'update' | 'pause' | 'resume' | 'run' | 'delete', params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<{ harness: string; verb: string; ran: string; id: string; job?: RecordedJob; deleted?: true }>;
|
|
170
|
+
export function mutateJobNotepad(verb: 'set' | 'delete', params: unknown, context?: SessionReadContext & { signal?: AbortSignal }): Promise<ReturnType<typeof readJobNotepad> & { ran: string }>;
|