@letta-ai/letta-code 0.27.19 → 0.27.20
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/letta.js +92 -10
- package/package.json +1 -1
- package/skills/creating-mods/references/events.md +8 -2
package/letta.js
CHANGED
|
@@ -4790,7 +4790,7 @@ var package_default;
|
|
|
4790
4790
|
var init_package = __esm(() => {
|
|
4791
4791
|
package_default = {
|
|
4792
4792
|
name: "@letta-ai/letta-code",
|
|
4793
|
-
version: "0.27.
|
|
4793
|
+
version: "0.27.20",
|
|
4794
4794
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
4795
4795
|
type: "module",
|
|
4796
4796
|
packageManager: "bun@1.3.0",
|
|
@@ -170051,7 +170051,13 @@ var init_system_prompt_warning = __esm(() => {
|
|
|
170051
170051
|
});
|
|
170052
170052
|
|
|
170053
170053
|
// src/websocket/listener/constants.ts
|
|
170054
|
-
|
|
170054
|
+
function isListenerPongStale(lastPongAt, now, timeoutMs) {
|
|
170055
|
+
if (lastPongAt === null) {
|
|
170056
|
+
return false;
|
|
170057
|
+
}
|
|
170058
|
+
return now - lastPongAt > timeoutMs;
|
|
170059
|
+
}
|
|
170060
|
+
var MAX_RETRY_DURATION_MS, INITIAL_RETRY_DELAY_MS2 = 1000, MAX_RETRY_DELAY_MS = 30000, LISTENER_HEARTBEAT_INTERVAL_MS = 30000, LISTENER_PONG_TIMEOUT_MS = 90000, SYSTEM_REMINDER_RE, LLM_API_ERROR_MAX_RETRIES = 3, EMPTY_RESPONSE_MAX_RETRIES = 2, MAX_PRE_STREAM_RECOVERY = 2, MAX_POST_STOP_APPROVAL_RECOVERY = 2, PROVIDER_FALLBACK_MAP, PROVIDER_FALLBACK_NOTICE = "Anthropic API error; falling back to Bedrock...";
|
|
170055
170061
|
var init_constants3 = __esm(() => {
|
|
170056
170062
|
MAX_RETRY_DURATION_MS = 5 * 60 * 1000;
|
|
170057
170063
|
SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
@@ -381853,6 +381859,18 @@ function isModelOutputEvent(event2) {
|
|
|
381853
381859
|
return false;
|
|
381854
381860
|
}
|
|
381855
381861
|
}
|
|
381862
|
+
function llmEndErrorFromError(error54) {
|
|
381863
|
+
const info = normalizeLocalProviderError(error54);
|
|
381864
|
+
return {
|
|
381865
|
+
error: {
|
|
381866
|
+
message: info.message,
|
|
381867
|
+
detail: info.detail,
|
|
381868
|
+
errorType: info.error_type,
|
|
381869
|
+
retryable: info.retryable
|
|
381870
|
+
},
|
|
381871
|
+
stopReason: info.stop_reason
|
|
381872
|
+
};
|
|
381873
|
+
}
|
|
381856
381874
|
function isOverflowError(error54, contextWindow) {
|
|
381857
381875
|
if (error54 instanceof PiProviderError) {
|
|
381858
381876
|
return isContextOverflow(error54.assistant, contextWindow);
|
|
@@ -382027,6 +382045,17 @@ class PiStreamAdapter {
|
|
|
382027
382045
|
}
|
|
382028
382046
|
const restoreEnv = applyPiEnvOverrides(resolved.envOverrides);
|
|
382029
382047
|
const llmStartedAt = Date.now();
|
|
382048
|
+
let llmEnded = false;
|
|
382049
|
+
const emitLlmEnd = async (info) => {
|
|
382050
|
+
llmEnded = true;
|
|
382051
|
+
await this.onLlmEnd?.({
|
|
382052
|
+
agentId: input.agentId,
|
|
382053
|
+
conversationId: input.conversationId,
|
|
382054
|
+
model: input.agent.model,
|
|
382055
|
+
durationMs: Date.now() - llmStartedAt,
|
|
382056
|
+
...info
|
|
382057
|
+
});
|
|
382058
|
+
};
|
|
382030
382059
|
try {
|
|
382031
382060
|
await this.onLlmStart?.({
|
|
382032
382061
|
agentId: input.agentId,
|
|
@@ -382055,17 +382084,15 @@ class PiStreamAdapter {
|
|
|
382055
382084
|
if (streamError)
|
|
382056
382085
|
throw streamError;
|
|
382057
382086
|
finalMessage ??= await result.result();
|
|
382058
|
-
|
|
382059
|
-
|
|
382060
|
-
conversationId: input.conversationId,
|
|
382061
|
-
model: input.agent.model,
|
|
382087
|
+
const finalMessageError = finalMessage.stopReason === "error" || finalMessage.stopReason === "aborted" ? llmEndErrorFromError(new PiProviderError(finalMessage)).error : undefined;
|
|
382088
|
+
await emitLlmEnd({
|
|
382062
382089
|
stopReason: finalMessage.stopReason,
|
|
382063
382090
|
usage: {
|
|
382064
382091
|
promptTokens: finalMessage.usage.input,
|
|
382065
382092
|
completionTokens: finalMessage.usage.output,
|
|
382066
382093
|
totalTokens: finalMessage.usage.totalTokens
|
|
382067
382094
|
},
|
|
382068
|
-
|
|
382095
|
+
...finalMessageError ? { error: finalMessageError } : {}
|
|
382069
382096
|
});
|
|
382070
382097
|
if (finalMessage.stopReason === "error" || finalMessage.stopReason === "aborted") {
|
|
382071
382098
|
throw new PiProviderError(finalMessage);
|
|
@@ -382076,6 +382103,16 @@ class PiStreamAdapter {
|
|
|
382076
382103
|
yield* this.emitCompactionChunks(compaction, "context_window_limit");
|
|
382077
382104
|
}
|
|
382078
382105
|
}
|
|
382106
|
+
} catch (error54) {
|
|
382107
|
+
if (!llmEnded) {
|
|
382108
|
+
const endError = llmEndErrorFromError(error54);
|
|
382109
|
+
await emitLlmEnd({
|
|
382110
|
+
stopReason: endError.stopReason,
|
|
382111
|
+
usage: null,
|
|
382112
|
+
error: endError.error
|
|
382113
|
+
});
|
|
382114
|
+
}
|
|
382115
|
+
throw error54;
|
|
382079
382116
|
} finally {
|
|
382080
382117
|
restoreEnv();
|
|
382081
382118
|
}
|
|
@@ -445547,6 +445584,9 @@ function createListenerMessageHandler(params) {
|
|
|
445547
445584
|
try {
|
|
445548
445585
|
const lifecycleMessage = parseServerLifecycleMessage(data);
|
|
445549
445586
|
if (lifecycleMessage) {
|
|
445587
|
+
if (lifecycleMessage.type === "pong") {
|
|
445588
|
+
runtime.lastPongAt = Date.now();
|
|
445589
|
+
}
|
|
445550
445590
|
safeEmitWsEvent("recv", "lifecycle", lifecycleMessage);
|
|
445551
445591
|
return;
|
|
445552
445592
|
}
|
|
@@ -446398,6 +446438,7 @@ function createRuntime() {
|
|
|
446398
446438
|
streamTransport: null,
|
|
446399
446439
|
heartbeatInterval: null,
|
|
446400
446440
|
reconnectTimeout: null,
|
|
446441
|
+
lastPongAt: null,
|
|
446401
446442
|
intentionallyClosed: false,
|
|
446402
446443
|
hasSuccessfulConnection: false,
|
|
446403
446444
|
everConnected: false,
|
|
@@ -446544,9 +446585,15 @@ async function startConnectedListenerRuntime(runtime, transport, opts, processQu
|
|
|
446544
446585
|
scheduleQueuePump(targetRuntime, transport, opts, processQueuedTurn);
|
|
446545
446586
|
});
|
|
446546
446587
|
if (shouldStartHeartbeat) {
|
|
446588
|
+
runtime.lastPongAt = Date.now();
|
|
446547
446589
|
runtime.heartbeatInterval = setInterval(() => {
|
|
446590
|
+
if (getListenerTransportKind(transport) === "websocket" && isListenerPongStale(runtime.lastPongAt, Date.now(), LISTENER_PONG_TIMEOUT_MS)) {
|
|
446591
|
+
trackListenerError4("listener_pong_timeout", new Error(`No relay pong within ${LISTENER_PONG_TIMEOUT_MS}ms; terminating half-open socket to force reconnect`), "listener_heartbeat");
|
|
446592
|
+
runtime.socket?.terminate();
|
|
446593
|
+
return;
|
|
446594
|
+
}
|
|
446548
446595
|
safeTransportSend(transport, { type: "ping" }, "listener_ping_send_failed", "listener_heartbeat");
|
|
446549
|
-
},
|
|
446596
|
+
}, LISTENER_HEARTBEAT_INTERVAL_MS);
|
|
446550
446597
|
}
|
|
446551
446598
|
if (shouldStartCronScheduler) {
|
|
446552
446599
|
startScheduler(transport, opts, processQueuedTurn);
|
|
@@ -449938,6 +449985,12 @@ function createLegacyTestRuntime() {
|
|
|
449938
449985
|
listener.heartbeatInterval = value;
|
|
449939
449986
|
}
|
|
449940
449987
|
},
|
|
449988
|
+
lastPongAt: {
|
|
449989
|
+
get: () => listener.lastPongAt,
|
|
449990
|
+
set: (value) => {
|
|
449991
|
+
listener.lastPongAt = value;
|
|
449992
|
+
}
|
|
449993
|
+
},
|
|
449941
449994
|
intentionallyClosed: {
|
|
449942
449995
|
get: () => listener.intentionallyClosed,
|
|
449943
449996
|
set: (value) => {
|
|
@@ -454084,7 +454137,8 @@ function installLocalBackendModEventHooks(options3) {
|
|
|
454084
454137
|
model: info.model,
|
|
454085
454138
|
stopReason: info.stopReason,
|
|
454086
454139
|
usage: info.usage,
|
|
454087
|
-
durationMs: info.durationMs
|
|
454140
|
+
durationMs: info.durationMs,
|
|
454141
|
+
...info.error ? { error: info.error } : {}
|
|
454088
454142
|
}, buildContext(info.conversationId));
|
|
454089
454143
|
}
|
|
454090
454144
|
});
|
|
@@ -524463,6 +524517,8 @@ await __promiseAll([
|
|
|
524463
524517
|
var DEFAULT_LISTEN_URL = "ws://127.0.0.1:0";
|
|
524464
524518
|
var DEFAULT_WS_PATH = "/ws";
|
|
524465
524519
|
var PENDING_STREAM_TIMEOUT_MS = 5000;
|
|
524520
|
+
var APP_SERVER_HEARTBEAT_INTERVAL_MS = 30000;
|
|
524521
|
+
var APP_SERVER_PONG_TIMEOUT_MS = 90000;
|
|
524466
524522
|
function getRequiredAddressInfo(server2) {
|
|
524467
524523
|
const address = server2.address();
|
|
524468
524524
|
if (!address || typeof address === "string") {
|
|
@@ -524598,6 +524654,7 @@ async function startAppServer(options3 = {}) {
|
|
|
524598
524654
|
let pendingStreamSocket = null;
|
|
524599
524655
|
let pendingStreamTimeout = null;
|
|
524600
524656
|
let resolvedInfo = null;
|
|
524657
|
+
const lastPongAtBySocket = new WeakMap;
|
|
524601
524658
|
const clearPendingStream = () => {
|
|
524602
524659
|
if (pendingStreamTimeout) {
|
|
524603
524660
|
clearTimeout(pendingStreamTimeout);
|
|
@@ -524608,6 +524665,10 @@ async function startAppServer(options3 = {}) {
|
|
|
524608
524665
|
return socket;
|
|
524609
524666
|
};
|
|
524610
524667
|
const handleWebSocketConnection = (socket, channel) => {
|
|
524668
|
+
lastPongAtBySocket.set(socket, Date.now());
|
|
524669
|
+
socket.on("pong", () => {
|
|
524670
|
+
lastPongAtBySocket.set(socket, Date.now());
|
|
524671
|
+
});
|
|
524611
524672
|
if (channel === "stream") {
|
|
524612
524673
|
if (activeSession) {
|
|
524613
524674
|
attachStreamSocket(activeSession, socket);
|
|
@@ -524706,6 +524767,26 @@ async function startAppServer(options3 = {}) {
|
|
|
524706
524767
|
handleWebSocketConnection(websocket, channel);
|
|
524707
524768
|
});
|
|
524708
524769
|
});
|
|
524770
|
+
const heartbeatIntervalMs = options3.heartbeatIntervalMs ?? APP_SERVER_HEARTBEAT_INTERVAL_MS;
|
|
524771
|
+
const pongTimeoutMs = options3.pongTimeoutMs ?? APP_SERVER_PONG_TIMEOUT_MS;
|
|
524772
|
+
const heartbeatInterval = setInterval(() => {
|
|
524773
|
+
const now = Date.now();
|
|
524774
|
+
for (const client of wss.clients) {
|
|
524775
|
+
const lastPongAt = lastPongAtBySocket.get(client) ?? now;
|
|
524776
|
+
if (now - lastPongAt > pongTimeoutMs) {
|
|
524777
|
+
options3.onLog?.(`App-server terminating unresponsive socket (no pong in ${pongTimeoutMs}ms)`);
|
|
524778
|
+
client.terminate();
|
|
524779
|
+
continue;
|
|
524780
|
+
}
|
|
524781
|
+
if (client.readyState === WebSocket7.OPEN) {
|
|
524782
|
+
client.ping();
|
|
524783
|
+
}
|
|
524784
|
+
}
|
|
524785
|
+
}, heartbeatIntervalMs);
|
|
524786
|
+
heartbeatInterval.unref?.();
|
|
524787
|
+
wss.on("close", () => {
|
|
524788
|
+
clearInterval(heartbeatInterval);
|
|
524789
|
+
});
|
|
524709
524790
|
await new Promise((resolve30, reject) => {
|
|
524710
524791
|
const onError = (error54) => {
|
|
524711
524792
|
server2.off("listening", onListening);
|
|
@@ -524730,6 +524811,7 @@ async function startAppServer(options3 = {}) {
|
|
|
524730
524811
|
return {
|
|
524731
524812
|
...resolvedInfo,
|
|
524732
524813
|
close: async () => {
|
|
524814
|
+
clearInterval(heartbeatInterval);
|
|
524733
524815
|
const streamSocket = clearPendingStream();
|
|
524734
524816
|
terminateSocket(streamSocket);
|
|
524735
524817
|
if (activeSession) {
|
|
@@ -533971,4 +534053,4 @@ Error during initialization: ${message}`);
|
|
|
533971
534053
|
}
|
|
533972
534054
|
main2();
|
|
533973
534055
|
|
|
533974
|
-
//# debugId=
|
|
534056
|
+
//# debugId=EC4DC695909CD41C64756E2164756E21
|
package/package.json
CHANGED
|
@@ -284,12 +284,18 @@ Handlers run in registration order. Later handlers see the current input after e
|
|
|
284
284
|
promptTokens: number;
|
|
285
285
|
completionTokens: number;
|
|
286
286
|
totalTokens: number;
|
|
287
|
-
};
|
|
287
|
+
} | null;
|
|
288
288
|
durationMs: number;
|
|
289
|
+
error?: {
|
|
290
|
+
message: string;
|
|
291
|
+
detail: string;
|
|
292
|
+
errorType: "llm_error" | "local_backend_error";
|
|
293
|
+
retryable: boolean;
|
|
294
|
+
};
|
|
289
295
|
}
|
|
290
296
|
```
|
|
291
297
|
|
|
292
|
-
`llm_end` fires
|
|
298
|
+
`llm_end` fires when a provider request ends, success or failure. Successful requests include token usage. Requests that fail before usage is available set `usage: null` and include `error`. Retry/failover effects are not supported yet; both events are notification-only and return values are ignored. A throwing handler is isolated and never breaks the provider request.
|
|
293
299
|
|
|
294
300
|
Handlers also receive:
|
|
295
301
|
|