-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsession-state.mjs
More file actions
143 lines (128 loc) · 5.62 KB
/
Copy pathsession-state.mjs
File metadata and controls
143 lines (128 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { deleteOptional, readOptionalText, writeText } from './runtime.mjs';
import { fs } from '@eliware/common';
const ONESHOT_PREFIX = '.agentx_responseid.oneshot-';
const STALE_ONESHOT_AGE_MS = 60 * 60 * 1000;
function normalizeUsage(usage = {}) {
return {
inputTokens: Number(usage.inputTokens ?? 0),
cachedTokens: Number(usage.cachedTokens ?? 0),
outputTokens: Number(usage.outputTokens ?? 0),
turns: Number(usage.turns ?? 0),
};
}
function normalizePendingToolCall(call) {
if (!call || typeof call !== 'object') return null;
try {
return JSON.parse(JSON.stringify(call));
} catch {
return {
type: String(call.type ?? 'function_call'),
name: call.name == null ? undefined : String(call.name),
call_id: String(call.call_id ?? call.id ?? ''),
input: call.input == null ? undefined : String(call.input),
arguments: call.arguments == null ? undefined : String(call.arguments),
};
}
}
function normalizeHistoryEntry(entry) {
if (!entry || typeof entry !== 'object') return null;
return {
response_id: String(entry.response_id ?? ''),
timestamp: String(entry.timestamp ?? ''),
user_preview: String(entry.user_preview ?? '').slice(0, 20),
assistant_preview: String(entry.assistant_preview ?? '').slice(0, 20),
usage: normalizeUsage(entry.usage),
last_user_message: String(entry.last_user_message ?? ''),
last_assistant_message: String(entry.last_assistant_message ?? ''),
};
}
function normalizeHistory(history) {
if (!Array.isArray(history)) return [];
return history.map(normalizeHistoryEntry).filter((entry) => entry?.response_id).slice(-20);
}
function normalizePendingToolCalls(calls) {
if (!Array.isArray(calls)) return [];
return calls.map(normalizePendingToolCall).filter(Boolean);
}
function normalizeExecutionJournal(records) {
if (!Array.isArray(records)) return [];
return records.filter((record) => record && typeof record === 'object').map((record) => ({
identity: String(record.identity ?? ''),
status: String(record.status ?? 'pending'),
response_id: String(record.response_id ?? ''),
updated_at: String(record.updated_at ?? ''),
})).filter((record) => record.identity);
}
function normalizeSessionState(state) {
const normalized = {
response_id: String(state?.response_id ?? ''),
usage: normalizeUsage(state?.usage),
last_user_message: String(state?.last_user_message ?? ''),
last_assistant_message: String(state?.last_assistant_message ?? ''),
pending_cli_transcript: String(state?.pending_cli_transcript ?? ''),
pending_tool_calls: normalizePendingToolCalls(state?.pending_tool_calls),
};
if (Object.prototype.hasOwnProperty.call(state || {}, 'execution_journal')) normalized.execution_journal = normalizeExecutionJournal(state.execution_journal);
if (Object.prototype.hasOwnProperty.call(state || {}, 'history')) normalized.history = normalizeHistory(state.history);
if (Object.prototype.hasOwnProperty.call(state || {}, 'rollback_backup')) normalized.rollback_backup = normalizeHistory(state.rollback_backup);
if (Object.prototype.hasOwnProperty.call(state || {}, 'failed_response')) normalized.failed_response = Boolean(state.failed_response);
if (Object.prototype.hasOwnProperty.call(state || {}, 'pending_retry_request')) normalized.pending_retry_request = state.pending_retry_request && typeof state.pending_retry_request === 'object' ? JSON.parse(JSON.stringify(state.pending_retry_request)) : null;
return normalized;
}
export async function cleanupStaleOneShotStates(directory, now = Date.now()) {
let entries;
try { entries = await fs.promises.readdir(directory, { withFileTypes: true }); }
catch (error) { if (error?.code === 'ENOENT') return 0; throw error; }
let removed = 0;
for (const entry of entries) {
if (!entry.isFile() || !entry.name.startsWith(ONESHOT_PREFIX)) continue;
const filePath = `${directory}/${entry.name}`;
const stat = await fs.promises.stat(filePath);
if (now - stat.mtimeMs < STALE_ONESHOT_AGE_MS) continue;
await deleteOptional(filePath);
removed += 1;
}
return removed;
}
export async function persistResponseState(statePath, state) {
await writeText(statePath, `${JSON.stringify(normalizeSessionState(state), null, 2)}\n`);
}
export async function clearSession(statePath) {
await deleteOptional(statePath);
}
export async function readLatestCheckpoint(checkpointPath, fallbackStatePath = '') {
const checkpoint = await readSessionState(checkpointPath);
if (checkpoint?.response_id) return checkpoint;
if (!fallbackStatePath) return null;
const state = await readSessionState(fallbackStatePath);
const entry = state?.history?.at(-1);
return entry?.response_id ? {
response_id: entry.response_id,
usage: entry.usage,
last_user_message: entry.last_user_message,
last_assistant_message: entry.last_assistant_message,
pending_cli_transcript: '',
pending_tool_calls: [],
history: [entry],
} : null;
}
export async function persistCheckpoint(checkpointPath, state) {
await persistResponseState(checkpointPath, {
response_id: state?.response_id,
usage: state?.usage,
last_user_message: state?.last_user_message,
last_assistant_message: state?.last_assistant_message,
pending_cli_transcript: '',
pending_tool_calls: [],
history: state?.history,
});
}
export async function readSessionState(statePath) {
const raw = await readOptionalText(statePath);
if (!raw) return null;
try {
const parsed = JSON.parse(raw);
if (parsed && typeof parsed === 'object') return normalizeSessionState(parsed);
} catch { }
return normalizeSessionState({ response_id: raw.trim() || '', usage: {} });
}