@volter/supercode-interchange 0.1.8 → 0.1.10
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/README.md +13 -0
- package/metadata.mjs +1 -1
- package/openclaw.mjs +2 -2
- package/package.json +1 -1
- package/session-read.d.ts +3 -1
- package/session-read.mjs +15 -2
- package/session-resume-instructions.mjs +54 -0
- package/source-files.mjs +1 -1
- package/source-layout.mjs +7 -1
- package/startup-prompts.mjs +64 -0
package/README.md
CHANGED
|
@@ -123,6 +123,19 @@ record selected-home answers and null malformed metadata through npm and
|
|
|
123
123
|
operator streams; concurrent path/record replacement and runtime writing remain
|
|
124
124
|
unqualified.
|
|
125
125
|
|
|
126
|
+
`sessions.resume_instructions` reads the complete selected transcript before
|
|
127
|
+
returning a structured launch. An explicit cwd wins over the transcript's
|
|
128
|
+
recorded cwd, then `.`; the locator supplies the harness and session id.
|
|
129
|
+
Default and explicit `yolo` policy use the shared generated startup table.
|
|
130
|
+
Grok receives its resolved default home only when that home is a symlink and
|
|
131
|
+
no explicit `GROK_HOME` was supplied. This returns arguments and environment;
|
|
132
|
+
it neither starts a harness nor writes a session or permission configuration.
|
|
133
|
+
The separate `resume_instructions` capability marker refuses older services.
|
|
134
|
+
The [private Mac/Windows record](../../docs/evidence/session-resume-instructions-2026-10-08.md)
|
|
135
|
+
retains eight harnesses' specifications, cwd/environment cases and older-service
|
|
136
|
+
refusal through npm and loopback operator streams. Installed sets, actual live
|
|
137
|
+
resume and full-corpus qualification remain open.
|
|
138
|
+
|
|
126
139
|
`sessions.usage` reads a complete lossless session in the load worker and groups
|
|
127
140
|
recorded tokens by the native day prefix and model. Claude keeps the last usage
|
|
128
141
|
record for each response id; Codex separates successive cumulative totals and
|
package/metadata.mjs
CHANGED
|
@@ -119,7 +119,7 @@ export function headerFacts(records, harness) {
|
|
|
119
119
|
applyHeaderRecord(result, value, harness);
|
|
120
120
|
if (headerComplete(result, harness)) break;
|
|
121
121
|
}
|
|
122
|
-
if (result.session_id == null && result.cwd == null) throw new Error(`no recognizable ${harness} session header`);
|
|
122
|
+
if (result.session_id == null && result.cwd == null) throw Object.assign(new Error(`no recognizable ${harness} session header`), { code: 'not_a_session' });
|
|
123
123
|
return result;
|
|
124
124
|
}
|
|
125
125
|
|
package/openclaw.mjs
CHANGED
|
@@ -44,7 +44,7 @@ async function scanCounts(reader, previous) {
|
|
|
44
44
|
headerPieces = []; headerSize = 0;
|
|
45
45
|
if (!nativeTrim(text)) return;
|
|
46
46
|
const value = parseJsonLine(bytes, { strictUtf8: true });
|
|
47
|
-
if (value?.type !== 'session') throw new Error(`${reader.path} has no OpenClaw session header`);
|
|
47
|
+
if (value?.type !== 'session') throw Object.assign(new Error(`${reader.path} has no OpenClaw session header`), { code: 'not_a_session' });
|
|
48
48
|
header = value; headerTerminated = terminated;
|
|
49
49
|
};
|
|
50
50
|
while (offset < reader.size) {
|
|
@@ -72,7 +72,7 @@ async function scanCounts(reader, previous) {
|
|
|
72
72
|
}
|
|
73
73
|
utf8.decode(); // A truncated multibyte sequence is a failed part, not none.
|
|
74
74
|
if (header == null && headerSize) acceptHeader(false);
|
|
75
|
-
if (header == null) throw new Error(`${reader.path} has no OpenClaw session header`);
|
|
75
|
+
if (header == null) throw Object.assign(new Error(`${reader.path} has no OpenClaw session header`), { code: 'not_a_session' });
|
|
76
76
|
count.match_tail = overlap.toString('base64');
|
|
77
77
|
return { count, header, header_terminated: headerTerminated };
|
|
78
78
|
}
|
package/package.json
CHANGED
package/session-read.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { SessionFollower } from './session-codecs.js';
|
|
|
3
3
|
import type { ProjectedSessionResult, NormalizedSession, RecordedSessionProfile, RecordedSessionProfileChange } from './session-model.js';
|
|
4
4
|
export const SESSION_READ_METHODS: readonly string[];
|
|
5
5
|
export const SESSION_READ_EVENT: 'harness.v1.sessions.event';
|
|
6
|
-
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 }>;
|
|
6
|
+
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 }>;
|
|
7
7
|
export interface SessionReadContext { cwd?: string; env?: Record<string, string | undefined> }
|
|
8
8
|
export function parseSessionWire(text: string): unknown;
|
|
9
9
|
export function stringifySessionWire(value: unknown): string;
|
|
@@ -32,3 +32,5 @@ export interface MachineTokenSpend extends RecordedTokenSpend {
|
|
|
32
32
|
}
|
|
33
33
|
/** Recently modified Claude/Codex tails, including child use on the parent. */
|
|
34
34
|
export function loadSessionSpend(params: unknown, context?: SessionReadContext): Promise<{ since: string; until: string; session_count: number; sessions: MachineTokenSpend[]; total: RecordedTokenSpend }>;
|
|
35
|
+
/** Reads the complete source and returns a specification; starts no process. */
|
|
36
|
+
export function loadSessionResumeInstructions(params: unknown, context?: SessionReadContext): Promise<{ launch: { cwd: string; program: string; arguments: string[]; env: Record<string, string> } }>;
|
package/session-read.mjs
CHANGED
|
@@ -12,11 +12,12 @@ import { recordedSessionConfiguration } from './session-recorded-config.mjs';
|
|
|
12
12
|
import { recordedLaunchAgent } from './session-launch-agent.mjs';
|
|
13
13
|
import { recordedSessionUsage } from './session-usage.mjs';
|
|
14
14
|
import { loadMachineSpend } from './session-spend.mjs';
|
|
15
|
+
import { sessionResumeLaunch } from './session-resume-instructions.mjs';
|
|
15
16
|
import { parseUsageSince, usageMoment } from './session-usage-time.mjs';
|
|
16
17
|
|
|
17
|
-
export const SESSION_READ_METHODS = Object.freeze(['harness.v1.sessions.load', 'harness.v1.sessions.follow', 'harness.v1.sessions.unfollow', 'harness.v1.sessions.recorded_config', 'harness.v1.sessions.launch_agent', 'harness.v1.sessions.usage', 'harness.v1.sessions.spend']);
|
|
18
|
+
export const SESSION_READ_METHODS = Object.freeze(['harness.v1.sessions.load', 'harness.v1.sessions.follow', 'harness.v1.sessions.unfollow', 'harness.v1.sessions.recorded_config', 'harness.v1.sessions.launch_agent', 'harness.v1.sessions.usage', 'harness.v1.sessions.spend', 'harness.v1.sessions.resume_instructions']);
|
|
18
19
|
export const SESSION_READ_EVENT = 'harness.v1.sessions.event';
|
|
19
|
-
export const SESSION_READ_CAPABILITY = Object.freeze({ version: 1, owner: 'caller', language: 'typescript', native_parser: false, recorded_config: true, launch_agent: true, usage: true, spend: true });
|
|
20
|
+
export const SESSION_READ_CAPABILITY = Object.freeze({ version: 1, owner: 'caller', language: 'typescript', native_parser: false, recorded_config: true, launch_agent: true, usage: true, spend: true, resume_instructions: true });
|
|
20
21
|
export { loadMachineSpend as loadSessionSpend };
|
|
21
22
|
export { parseNativeJson as parseSessionWire, stringifyNativeJson as stringifySessionWire };
|
|
22
23
|
export { stringifyNativeJsonPretty as prettySessionWire };
|
|
@@ -138,3 +139,15 @@ export async function loadSessionUsage(params, context = {}) {
|
|
|
138
139
|
if (since != null) answer.since = usageMoment(since);
|
|
139
140
|
return answer;
|
|
140
141
|
}
|
|
142
|
+
|
|
143
|
+
export async function loadSessionResumeInstructions(params, context = {}) {
|
|
144
|
+
object(params, 'resume instructions params');
|
|
145
|
+
const { locator } = parseSessionReadParams({ locator: params.locator }, context);
|
|
146
|
+
const cwd = params.cwd == null ? null : string(params.cwd, 'cwd');
|
|
147
|
+
const policy = Object.hasOwn(params, 'policy') ? params.policy : 'default';
|
|
148
|
+
if (!['default', 'yolo'].includes(policy)) invalid(`unknown resume policy: ${String(policy)}`);
|
|
149
|
+
// Native admission loads the source even when cwd is explicit, and its
|
|
150
|
+
// launch identity comes from the locator rather than a transcript header.
|
|
151
|
+
const session = await loadSessionLocator(locator, { ...context, fidelity: 'byte_lossless', includeSubagents: true });
|
|
152
|
+
return { launch: sessionResumeLaunch(locator.harness, locator.session_id, cwd ?? session.meta.cwd ?? '.', policy, context) };
|
|
153
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// A launch specification only. The caller decides whether to run the harness.
|
|
2
|
+
import { lstatSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { userInfo } from 'node:os';
|
|
4
|
+
import { isAbsolute } from 'node:path';
|
|
5
|
+
import { STARTUP_PROMPTS } from './startup-prompts.mjs';
|
|
6
|
+
import { childPath } from './session-spend-files.mjs';
|
|
7
|
+
|
|
8
|
+
function startupArguments(harness, cwd, unattended) {
|
|
9
|
+
const prompts = STARTUP_PROMPTS[harness];
|
|
10
|
+
if (!prompts) return [];
|
|
11
|
+
const where = JSON.stringify(cwd);
|
|
12
|
+
const arguments_ = prompts.startup.flatMap(pair => pair.map(flag => flag.replaceAll('{cwd}', where)));
|
|
13
|
+
if (unattended) arguments_.push(...prompts.unattended);
|
|
14
|
+
return arguments_;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function grokHomeEnvironment(env, cwd) {
|
|
18
|
+
if (env.GROK_HOME !== undefined) return {};
|
|
19
|
+
let home = env.HOME || (process.platform === 'win32' ? env.USERPROFILE : null);
|
|
20
|
+
if (!home) { try { home = userInfo().homedir; } catch { return {}; } }
|
|
21
|
+
if (!isAbsolute(home) && !(process.platform === 'win32' && /^[A-Za-z]:/.test(home))) home = childPath(cwd, home);
|
|
22
|
+
const path = childPath(home, '.grok');
|
|
23
|
+
try { if (lstatSync(path).isSymbolicLink()) return { GROK_HOME: realpathSync(path) }; }
|
|
24
|
+
catch { /* no recorded filesystem resolution to supply */ }
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sessionResumeLaunch(harness, sessionId, cwd, policy, { env = process.env, cwd: readCwd = process.cwd() } = {}) {
|
|
29
|
+
const unattended = policy === 'yolo';
|
|
30
|
+
let program, arguments_;
|
|
31
|
+
switch (harness) {
|
|
32
|
+
case 'grok':
|
|
33
|
+
program = 'grok';
|
|
34
|
+
arguments_ = [...(unattended ? ['--sandbox', 'workspace', '--always-approve'] : []), '--resume', sessionId];
|
|
35
|
+
break;
|
|
36
|
+
case 'codex':
|
|
37
|
+
program = 'codex'; arguments_ = [...startupArguments(harness, cwd, unattended), 'resume', sessionId]; break;
|
|
38
|
+
case 'claude-code':
|
|
39
|
+
program = 'claude'; arguments_ = [...startupArguments(harness, cwd, unattended), '--resume', sessionId]; break;
|
|
40
|
+
case 'gemini':
|
|
41
|
+
program = 'gemini'; arguments_ = [...startupArguments(harness, cwd, unattended), '--resume', sessionId]; break;
|
|
42
|
+
case 'goose':
|
|
43
|
+
program = 'goose'; arguments_ = ['session', '--resume', '--session-id', sessionId]; break;
|
|
44
|
+
case 'pi':
|
|
45
|
+
program = 'pi'; arguments_ = [...startupArguments(harness, cwd, unattended), '--session', sessionId]; break;
|
|
46
|
+
case 'opencode':
|
|
47
|
+
program = 'opencode'; arguments_ = ['--session', sessionId]; break;
|
|
48
|
+
case 'supercode':
|
|
49
|
+
program = 'supercode'; arguments_ = [...(unattended ? ['--dangerous'] : []), 'resume', sessionId]; break;
|
|
50
|
+
default:
|
|
51
|
+
throw Object.assign(new Error(`no structured resume launch is registered for harness \`${harness}\``), { rpcCode: -32602 });
|
|
52
|
+
}
|
|
53
|
+
return { cwd, program, arguments: arguments_, env: program === 'grok' ? grokHomeEnvironment(env, readCwd) : {} };
|
|
54
|
+
}
|
package/source-files.mjs
CHANGED
|
@@ -46,7 +46,7 @@ async function geminiHeader(reader) {
|
|
|
46
46
|
const bytes = await reader.read(0, Math.min(64 * 1024, reader.size));
|
|
47
47
|
const newline = bytes.indexOf(10), end = newline < 0 ? bytes.length : newline;
|
|
48
48
|
const header = parseJsonLine(bytes.subarray(0, end), { strictUtf8: true });
|
|
49
|
-
if (typeof header?.sessionId !== 'string') throw new Error(`${reader.path} has no Gemini sessionId header`);
|
|
49
|
+
if (typeof header?.sessionId !== 'string') throw Object.assign(new Error(`${reader.path} has no Gemini sessionId header`), { code: 'not_a_session' });
|
|
50
50
|
const after = newline < 0 ? bytes.length : newline + 1;
|
|
51
51
|
const decoration = bytes.subarray(after, Math.min(bytes.length, after + 4 * 1024));
|
|
52
52
|
let model = null, modelTerminated = false;
|
package/source-layout.mjs
CHANGED
|
@@ -37,7 +37,13 @@ export async function sessionPartFor(harness, root, path) {
|
|
|
37
37
|
const name = basename(path);
|
|
38
38
|
if (harness === 'claude-code' || harness === 'pi') return name.endsWith('.jsonl') ? path : null;
|
|
39
39
|
if (harness === 'codex') return name.endsWith('.jsonl') || name.endsWith('.jsonl.zst') ? path : null;
|
|
40
|
-
|
|
40
|
+
// OpenClaw keeps its sessions in `agents/<id>/sessions/` only (native discover_openclaw); the rest of its state
|
|
41
|
+
// folder (logs, cron, workspace) holds JSONL that is not a session.
|
|
42
|
+
if (harness === 'openclaw') {
|
|
43
|
+
const part = relative(root, path).split(sep);
|
|
44
|
+
return part.length === 4 && part[0] === 'agents' && part[2] === 'sessions'
|
|
45
|
+
&& name.endsWith('.jsonl') && !name.endsWith('.trajectory.jsonl') ? path : null;
|
|
46
|
+
}
|
|
41
47
|
if (harness === 'gemini') return name.endsWith('.jsonl') && pathUnder(join(root, 'tmp'), path) ? path : null;
|
|
42
48
|
if (harness === 'grok') {
|
|
43
49
|
const part = relative(root, path).split(sep);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Generated from crates/harness/src/startup_prompts.json by scripts/startup-prompts.mjs (the pre-commit hook runs it):
|
|
2
|
+
// edit that table, not this file.
|
|
3
|
+
export const STARTUP_PROMPTS = Object.freeze({
|
|
4
|
+
"claude-code": {
|
|
5
|
+
"startup": [],
|
|
6
|
+
"unattended": [
|
|
7
|
+
"--dangerously-skip-permissions"
|
|
8
|
+
],
|
|
9
|
+
"unattended_chosen_by": [
|
|
10
|
+
"--permission-mode",
|
|
11
|
+
"--permission-prompts",
|
|
12
|
+
"--dangerously-skip-permissions",
|
|
13
|
+
"--allow-dangerously-skip-permissions"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"codex": {
|
|
17
|
+
"startup": [
|
|
18
|
+
[
|
|
19
|
+
"-c",
|
|
20
|
+
"check_for_update_on_startup=false"
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
"-c",
|
|
24
|
+
"projects={{cwd}={trust_level=\"trusted\"}}"
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
"--no-alt-screen"
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"--dangerously-bypass-hook-trust"
|
|
31
|
+
]
|
|
32
|
+
],
|
|
33
|
+
"unattended": [
|
|
34
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
35
|
+
],
|
|
36
|
+
"unattended_chosen_by": [
|
|
37
|
+
"-a",
|
|
38
|
+
"--ask-for-approval",
|
|
39
|
+
"-s",
|
|
40
|
+
"--sandbox",
|
|
41
|
+
"--approve-for-me",
|
|
42
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"gemini": {
|
|
46
|
+
"startup": [],
|
|
47
|
+
"unattended": [
|
|
48
|
+
"--yolo"
|
|
49
|
+
],
|
|
50
|
+
"unattended_chosen_by": [
|
|
51
|
+
"--yolo",
|
|
52
|
+
"--approval-mode"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"pi": {
|
|
56
|
+
"startup": [],
|
|
57
|
+
"unattended": [
|
|
58
|
+
"--approve"
|
|
59
|
+
],
|
|
60
|
+
"unattended_chosen_by": [
|
|
61
|
+
"--approve"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
});
|