fix: use node:crypto import for Node 18 compatibility (#1083)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2026-02-21 12:20:18 -08:00
committed by GitHub
parent 04e3d8739e
commit 00aa038681
4 changed files with 66 additions and 27 deletions

View File

@@ -181,6 +181,40 @@ jobs:
if: ${{ github.event_name != 'push' }}
run: bun pm pack
node18-smoke:
needs: check
name: Node 18 Smoke Test
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.0
- name: Install dependencies
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bun install
- name: Build bundle
run: bun run build
- name: Setup Node 18
uses: actions/setup-node@v6
with:
node-version: "18"
- name: Verify Node version
run: node --version
- name: CLI smoke test (Node 18)
run: node ./letta.js --help
headless:
needs: check
name: Headless / ${{ matrix.model }}

View File

@@ -26,6 +26,9 @@
"url": "https://github.com/letta-ai/letta-code.git"
},
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"publishConfig": {
"access": "public"
},

View File

@@ -1,3 +1,4 @@
import { randomUUID } from "node:crypto";
import { parseArgs } from "node:util";
import type { Letta } from "@letta-ai/letta-client";
import { APIError } from "@letta-ai/letta-client/core/error";
@@ -1398,7 +1399,7 @@ ${SYSTEM_REMINDER_CLOSE}
message: `Maximum turns limit reached (${buffers.usage.stepCount}/${maxTurns} steps)`,
stop_reason: "max_steps",
session_id: sessionId,
uuid: `error-max-turns-${crypto.randomUUID()}`,
uuid: `error-max-turns-${randomUUID()}`,
};
console.log(JSON.stringify(errorMsg));
} else {
@@ -1472,7 +1473,7 @@ ${SYSTEM_REMINDER_CLOSE}
message:
"Detected pending approval conflict on send; resolving before retry",
session_id: sessionId,
uuid: `recovery-pre-stream-${crypto.randomUUID()}`,
uuid: `recovery-pre-stream-${randomUUID()}`,
};
console.log(JSON.stringify(recoveryMsg));
} else {
@@ -1498,7 +1499,7 @@ ${SYSTEM_REMINDER_CLOSE}
max_attempts: CONVERSATION_BUSY_MAX_RETRIES,
delay_ms: CONVERSATION_BUSY_RETRY_DELAY_MS,
session_id: sessionId,
uuid: `retry-conversation-busy-${crypto.randomUUID()}`,
uuid: `retry-conversation-busy-${randomUUID()}`,
};
console.log(JSON.stringify(retryMsg));
} else {
@@ -1534,7 +1535,7 @@ ${SYSTEM_REMINDER_CLOSE}
max_attempts: LLM_API_ERROR_MAX_RETRIES,
delay_ms: delayMs,
session_id: sessionId,
uuid: `retry-pre-stream-${crypto.randomUUID()}`,
uuid: `retry-pre-stream-${randomUUID()}`,
};
console.log(JSON.stringify(retryMsg));
} else {
@@ -1586,7 +1587,7 @@ ${SYSTEM_REMINDER_CLOSE}
stop_reason: "error",
run_id: errorInfo.run_id,
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
...(errorInfo.error_type &&
errorInfo.run_id && {
api_error: {
@@ -1616,7 +1617,7 @@ ${SYSTEM_REMINDER_CLOSE}
"Detected pending approval conflict; auto-denying stale approval and retrying",
run_id: recoveryRunId ?? undefined,
session_id: sessionId,
uuid: `recovery-${recoveryRunId || crypto.randomUUID()}`,
uuid: `recovery-${recoveryRunId || randomUUID()}`,
};
console.log(JSON.stringify(recoveryMsg));
approvalPendingRecovery = true;
@@ -1670,7 +1671,7 @@ ${SYSTEM_REMINDER_CLOSE}
type: "stream_event",
event: chunk,
session_id: sessionId,
uuid: uuid || crypto.randomUUID(),
uuid: uuid || randomUUID(),
};
console.log(JSON.stringify(streamEvent));
} else {
@@ -1678,7 +1679,7 @@ ${SYSTEM_REMINDER_CLOSE}
type: "message",
...chunk,
session_id: sessionId,
uuid: uuid || crypto.randomUUID(),
uuid: uuid || randomUUID(),
};
console.log(JSON.stringify(msg));
}
@@ -1866,7 +1867,7 @@ ${SYSTEM_REMINDER_CLOSE}
delay_ms: delayMs,
run_id: lastRunId ?? undefined,
session_id: sessionId,
uuid: `retry-${lastRunId || crypto.randomUUID()}`,
uuid: `retry-${lastRunId || randomUUID()}`,
};
console.log(JSON.stringify(retryMsg));
} else {
@@ -1898,7 +1899,7 @@ ${SYSTEM_REMINDER_CLOSE}
"Tool call ID mismatch; fetching actual pending approvals and resyncing",
run_id: lastRunId ?? undefined,
session_id: sessionId,
uuid: `recovery-${lastRunId || crypto.randomUUID()}`,
uuid: `recovery-${lastRunId || randomUUID()}`,
};
console.log(JSON.stringify(recoveryMsg));
} else {
@@ -1921,7 +1922,7 @@ ${SYSTEM_REMINDER_CLOSE}
stop_reason: stopReason,
run_id: lastRunId ?? undefined,
session_id: sessionId,
uuid: `error-${lastRunId || crypto.randomUUID()}`,
uuid: `error-${lastRunId || randomUUID()}`,
};
console.log(JSON.stringify(errorMsg));
} else {
@@ -1984,7 +1985,7 @@ ${SYSTEM_REMINDER_CLOSE}
delay_ms: delayMs,
run_id: lastRunId ?? undefined,
session_id: sessionId,
uuid: `retry-${lastRunId || crypto.randomUUID()}`,
uuid: `retry-${lastRunId || randomUUID()}`,
};
console.log(JSON.stringify(retryMsg));
} else {
@@ -2051,7 +2052,7 @@ ${SYSTEM_REMINDER_CLOSE}
stop_reason: stopReason,
run_id: lastRunId ?? undefined,
session_id: sessionId,
uuid: `error-${lastRunId || crypto.randomUUID()}`,
uuid: `error-${lastRunId || randomUUID()}`,
};
console.log(JSON.stringify(errorMsg));
} else {
@@ -2073,7 +2074,7 @@ ${SYSTEM_REMINDER_CLOSE}
stop_reason: "error",
run_id: lastKnownRunId ?? undefined,
session_id: sessionId,
uuid: `error-${lastKnownRunId || crypto.randomUUID()}`,
uuid: `error-${lastKnownRunId || randomUUID()}`,
};
console.log(JSON.stringify(errorMsg));
} else {
@@ -2555,7 +2556,7 @@ async function runBidirectionalMode(
message: "Invalid JSON input",
stop_reason: "error",
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(errorMsg));
continue;
@@ -2586,7 +2587,7 @@ async function runBidirectionalMode(
},
},
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(initResponse));
} else if (subtype === "interrupt") {
@@ -2602,7 +2603,7 @@ async function runBidirectionalMode(
request_id: requestId ?? "",
},
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(interruptResponse));
} else if (subtype === "register_external_tools") {
@@ -2666,7 +2667,7 @@ async function runBidirectionalMode(
response: { registered: tools.length },
},
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(registerResponse));
} else {
@@ -2678,7 +2679,7 @@ async function runBidirectionalMode(
error: `Unknown control request subtype: ${subtype}`,
},
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(errorResponse));
}
@@ -2863,7 +2864,7 @@ async function runBidirectionalMode(
message:
"Detected pending approval conflict on send; resolving before retry",
session_id: sessionId,
uuid: `recovery-bidir-${crypto.randomUUID()}`,
uuid: `recovery-bidir-${randomUUID()}`,
};
console.log(JSON.stringify(recoveryMsg));
await resolveAllPendingApprovals();
@@ -2888,7 +2889,7 @@ async function runBidirectionalMode(
max_attempts: LLM_API_ERROR_MAX_RETRIES,
delay_ms: delayMs,
session_id: sessionId,
uuid: `retry-bidir-${crypto.randomUUID()}`,
uuid: `retry-bidir-${randomUUID()}`,
};
console.log(JSON.stringify(retryMsg));
@@ -2913,7 +2914,7 @@ async function runBidirectionalMode(
stop_reason: "error",
run_id: errorInfo.run_id,
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
...(errorInfo.error_type &&
errorInfo.run_id && {
api_error: {
@@ -2944,7 +2945,7 @@ async function runBidirectionalMode(
type: "stream_event",
event: chunk,
session_id: sessionId,
uuid: uuid || crypto.randomUUID(),
uuid: uuid || randomUUID(),
};
console.log(JSON.stringify(streamEvent));
} else {
@@ -2952,7 +2953,7 @@ async function runBidirectionalMode(
type: "message",
...chunk,
session_id: sessionId,
uuid: uuid || crypto.randomUUID(),
uuid: uuid || randomUUID(),
};
console.log(JSON.stringify(msg));
}
@@ -3218,7 +3219,7 @@ async function runBidirectionalMode(
message: errorDetails,
stop_reason: "error",
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(errorMsg));
@@ -3251,7 +3252,7 @@ async function runBidirectionalMode(
message: `Unknown message type: ${message.type}`,
stop_reason: "error",
session_id: sessionId,
uuid: crypto.randomUUID(),
uuid: randomUUID(),
};
console.log(JSON.stringify(errorMsg));
}

View File

@@ -1,6 +1,7 @@
// src/settings-manager.ts
// In-memory settings manager that loads once and provides sync access
import { randomUUID } from "node:crypto";
import { homedir } from "node:os";
import { join } from "node:path";
import type { HooksConfig } from "./hooks/types";
@@ -441,7 +442,7 @@ class SettingsManager {
const settings = this.getSettings();
let deviceId = settings.deviceId;
if (!deviceId) {
deviceId = crypto.randomUUID();
deviceId = randomUUID();
this.updateSettings({ deviceId });
}
return deviceId;