Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
456 changes: 237 additions & 219 deletions dist/index.js

Large diffs are not rendered by default.

153 changes: 133 additions & 20 deletions dist/src/llm-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,36 @@ function createHostClient(config, runtimeLlmComplete, log, warnLog) {
return null;
}
},
async completeText(prompt, label = "generic", systemPrompt, temperature) {
lastError = null;
const messages = [];
if (systemPrompt !== undefined)
messages.push({ role: "system", content: systemPrompt });
messages.push({ role: "user", content: prompt });
try {
const result = await raceWithTimeout(runtimeLlmComplete({
messages,
...(config.modelExplicit ? { model: config.model } : {}),
temperature: temperature ?? 0.1,
purpose: `memory-lancedb-pro:${label}`,
reasoning: config.thinkLevel?.trim() || DEFAULT_HOST_REASONING_EFFORT,
}), config.timeoutMs);
const text = typeof result?.text === "string" ? result.text.trim() : "";
if (!text) {
lastError =
`memory-lancedb-pro: llm-client [${label}] empty host-transport response content from model ${config.model}`;
log(lastError);
return null;
}
return text;
}
catch (err) {
lastError =
`memory-lancedb-pro: llm-client [${label}] host-transport request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`;
(warnLog ?? log)(lastError);
return null;
}
},
getLastError() {
return lastError;
},
Expand Down Expand Up @@ -394,6 +424,40 @@ function createApiKeyClient(config, log, warnLog) {
return null;
}
},
async completeText(prompt, label = "generic", systemPrompt, temperature) {
lastError = null;
try {
const request = {
model: config.model,
messages: [
...(systemPrompt !== undefined ? [{ role: "system", content: systemPrompt }] : []),
{ role: "user", content: prompt },
],
temperature: temperature ?? 0.1,
...(config.thinkLevel?.trim()
? { reasoning: { effort: config.thinkLevel.trim() } }
: {}),
};
const response = await client.chat.completions.create(request, {
headers: { "x-memory-call-label": sanitizeLabelHeader(label) },
});
const raw = response.choices?.[0]?.message?.content;
const text = typeof raw === "string" ? raw.trim() : "";
if (!text) {
lastError =
`memory-lancedb-pro: llm-client [${label}] empty response content from model ${config.model}`;
log(lastError);
return null;
}
return text;
}
catch (err) {
lastError =
`memory-lancedb-pro: llm-client [${label}] request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`;
(warnLog ?? log)(lastError);
return null;
}
},
getLastError() {
return lastError;
},
Expand Down Expand Up @@ -464,26 +528,7 @@ function createOauthClient(config, log, warnLog) {
const detail = await response.text().catch(() => "");
throw new Error(`HTTP ${response.status} ${response.statusText}: ${detail.slice(0, 500)}`);
}
const bodyText = await response.text();
const raw = (response.headers.get("content-type")?.includes("text/event-stream") ||
looksLikeSseResponse(bodyText))
? extractOutputTextFromSse(bodyText)
: (() => {
try {
const parsed = JSON.parse(bodyText);
const output = Array.isArray(parsed.output) ? parsed.output : [];
const first = output.find((item) => item &&
typeof item === "object" &&
Array.isArray(item.content));
if (!first)
return null;
const content = first.content.find((part) => part?.type === "output_text" && typeof part.text === "string");
return typeof content?.text === "string" ? content.text : null;
}
catch {
return null;
}
})();
const raw = extractOauthOutputText(response, await response.text());
if (!raw) {
lastError =
`memory-lancedb-pro: llm-client [${label}] empty OAuth response content from model ${config.model}`;
Expand Down Expand Up @@ -532,11 +577,79 @@ function createOauthClient(config, log, warnLog) {
return null;
}
},
async completeText(prompt, label = "generic", systemPrompt, _temperature) {
lastError = null;
try {
const session = await getSession();
const { signal, dispose } = createTimeoutSignal(config.timeoutMs);
const endpoint = buildOauthEndpoint(config.baseURL, config.oauthProvider);
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${session.accessToken}`,
"Content-Type": "application/json",
Accept: "text/event-stream",
"OpenAI-Beta": "responses=experimental",
"chatgpt-account-id": session.accountId,
originator: "codex_cli_rs",
},
signal,
body: JSON.stringify({
model: normalizeOauthModel(config.model),
...(systemPrompt !== undefined ? { instructions: systemPrompt } : {}),
input: [{ role: "user", content: [{ type: "input_text", text: prompt }] }],
store: false,
stream: true,
text: { format: { type: "text" } },
}),
});
if (!response.ok) {
const detail = await response.text().catch(() => "");
throw new Error(`HTTP ${response.status} ${response.statusText}: ${detail.slice(0, 500)}`);
}
const text = (extractOauthOutputText(response, await response.text()) ?? "").trim();
if (!text) {
lastError =
`memory-lancedb-pro: llm-client [${label}] empty OAuth response content from model ${config.model}`;
log(lastError);
return null;
}
return text;
}
finally {
dispose();
}
}
catch (err) {
lastError =
`memory-lancedb-pro: llm-client [${label}] OAuth request failed for model ${config.model}: ${err instanceof Error ? err.message : String(err)}`;
(warnLog ?? log)(lastError);
return null;
}
},
getLastError() {
return lastError;
},
};
}
function extractOauthOutputText(response, bodyText) {
if (response.headers.get("content-type")?.includes("text/event-stream") || looksLikeSseResponse(bodyText)) {
return extractOutputTextFromSse(bodyText);
}
try {
const parsed = JSON.parse(bodyText);
const output = Array.isArray(parsed.output) ? parsed.output : [];
const first = output.find((item) => item && typeof item === "object" && Array.isArray(item.content));
if (!first)
return null;
const content = first.content.find((part) => part?.type === "output_text" && typeof part.text === "string");
return typeof content?.text === "string" ? content.text : null;
}
catch {
return null;
}
}
/** OpenRouter's direct API base URL, used as the host->direct fallback's default when llm.baseURL is not configured. */
// Module-level (not per-client) so the "runtime surface unavailable"
// warning is emitted once per process even though createLlmClient is
Expand Down
53 changes: 33 additions & 20 deletions dist/src/session-recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,46 @@ function deriveOpenClawHomeFromSessionFilePath(sessionFilePath) {
const home = matched[1].trim();
return home.length ? home : undefined;
}
function listConfiguredAgentIds(cfg) {
/**
* Agent definitions come as `agents.list` (an array of `{id, workspace}`) on
* older hosts and as `agents.entries` (an object keyed by agent id) on newer
* ones; both shapes are read so neither generation loses its sessions dirs.
*/
function listConfiguredAgents(cfg) {
try {
const root = cfg;
const agents = root.agents;
const out = [];
const list = agents?.list;
if (!Array.isArray(list))
return [];
const ids = [];
for (const item of list) {
if (!item || typeof item !== "object")
continue;
const id = asNonEmptyString(item.id);
if (id)
ids.push(id);
if (Array.isArray(list)) {
for (const item of list) {
if (!item || typeof item !== "object")
continue;
const record = item;
out.push({ id: asNonEmptyString(record.id), workspace: asNonEmptyString(record.workspace) });
}
}
const entries = agents?.entries;
if (entries && typeof entries === "object" && !Array.isArray(entries)) {
for (const [key, item] of Object.entries(entries)) {
const record = item && typeof item === "object" ? item : {};
out.push({ id: asNonEmptyString(record.id) ?? asNonEmptyString(key), workspace: asNonEmptyString(record.workspace) });
}
}
return ids;
return out;
}
catch {
return [];
}
}
function listConfiguredAgentIds(cfg) {
const ids = [];
for (const agent of listConfiguredAgents(cfg)) {
if (agent.id && !ids.includes(agent.id))
ids.push(agent.id);
}
return ids;
}
export function resolveReflectionSessionSearchDirs(params) {
const out = [];
const seen = new Set();
Expand Down Expand Up @@ -103,15 +122,9 @@ export function resolveReflectionSessionSearchDirs(params) {
const defaultWorkspace = asNonEmptyString(defaults?.workspace);
if (defaultWorkspace)
addHome(openclawHomes, deriveOpenClawHomeFromWorkspacePath(defaultWorkspace));
const list = agents?.list;
if (Array.isArray(list)) {
for (const item of list) {
if (!item || typeof item !== "object")
continue;
const workspace = asNonEmptyString(item.workspace);
if (workspace)
addHome(openclawHomes, deriveOpenClawHomeFromWorkspacePath(workspace));
}
for (const agent of listConfiguredAgents(params.cfg)) {
if (agent.workspace)
addHome(openclawHomes, deriveOpenClawHomeFromWorkspacePath(agent.workspace));
}
}
catch {
Expand Down
Loading
Loading