@letta-ai/letta-code 0.30.26 → 0.30.27

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.
@@ -123,4 +123,37 @@ export declare function shouldAttemptApprovalRecovery(opts: {
123
123
  retries: number;
124
124
  maxRetries: number;
125
125
  }): boolean;
126
+ export interface ChatGPTUsageLimitDetail {
127
+ planType: string | null;
128
+ /** Absolute reset time in ms since epoch, when the server reported one. */
129
+ resetsAt: number | null;
130
+ }
131
+ /**
132
+ * Best-effort parse of a ChatGPT usage-limit error detail string, e.g.
133
+ * `ChatGPT rate limit exceeded: {"error":{"type":"usage_limit_reached",
134
+ * "plan_type":"plus","resets_at":1700000000,"resets_in_seconds":3600}}`.
135
+ * Returns null unless the detail is a string containing
136
+ * `usage_limit_reached` (case-insensitive); reset fields are optional and
137
+ * parse failures degrade to `{ planType: null, resetsAt: null }`.
138
+ */
139
+ export declare function parseChatGPTUsageLimitDetail(detail: unknown): ChatGPTUsageLimitDetail | null;
140
+ export interface ChatGPTFailoverModelEntry {
141
+ handle: string;
142
+ providerType?: string;
143
+ providerCategory?: string;
144
+ }
145
+ /**
146
+ * Pick a sibling ChatGPT plan handle to fail over to when the current plan
147
+ * hits its usage limit. The current handle must itself resolve to a
148
+ * chatgpt_oauth BYOK model in `models` (otherwise returns null). Siblings
149
+ * share the same model suffix (after the first `/`) under a different
150
+ * provider prefix, are chatgpt_oauth + byok, and are not in
151
+ * `exhaustedProviders`. One sibling is chosen uniformly at random.
152
+ */
153
+ export declare function selectChatGPTQuotaFailoverHandle(params: {
154
+ currentHandle: string;
155
+ models: ChatGPTFailoverModelEntry[];
156
+ exhaustedProviders: ReadonlySet<string>;
157
+ random?: () => number;
158
+ }): string | null;
126
159
  //# sourceMappingURL=turn-recovery-policy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"turn-recovery-policy.d.ts","sourceRoot":"","sources":["../../../src/agent/turn-recovery-policy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"turn-recovery-policy.d.ts","sourceRoot":"","sources":["../../../src/agent/turn-recovery-policy.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"apply-patch.d.ts","sourceRoot":"","sources":["../../../../src/tools/impl/apply-patch.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AA0CD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,gBAAgB,CAAC,CA8E3B"}
1
+ {"version":3,"file":"apply-patch.d.ts","sourceRoot":"","sources":["../../../../src/tools/impl/apply-patch.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AA0CD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,gBAAgB,CAAC,CA+E3B"}
package/letta.js CHANGED
@@ -5509,7 +5509,7 @@ var package_default;
5509
5509
  var init_package = __esm(() => {
5510
5510
  package_default = {
5511
5511
  name: "@letta-ai/letta-code",
5512
- version: "0.30.26",
5512
+ version: "0.30.27",
5513
5513
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5514
5514
  type: "module",
5515
5515
  packageManager: "bun@1.3.10",
@@ -150499,6 +150499,7 @@ async function apply_patch(args) {
150499
150499
  throw new Error("No files were modified.");
150500
150500
  }
150501
150501
  const cwd = getCurrentWorkingDirectory();
150502
+ verifyNoDuplicatePaths(cwd, operations);
150502
150503
  const affected = { added: [], modified: [], deleted: [] };
150503
150504
  for (const op of operations) {
150504
150505
  if (op.kind === "add") {
@@ -150761,6 +150762,17 @@ function assertPatchPath(patchPath, operation) {
150761
150762
  function resolvePatchPath(cwd, patchPath) {
150762
150763
  return path7.isAbsolute(patchPath) ? patchPath : path7.resolve(cwd, patchPath);
150763
150764
  }
150765
+ function verifyNoDuplicatePaths(cwd, operations) {
150766
+ const seen = new Set;
150767
+ for (const op of operations) {
150768
+ const patchPath = op.kind === "update" ? op.fromPath : op.path;
150769
+ const resolved = resolvePatchPath(cwd, patchPath);
150770
+ if (seen.has(resolved)) {
150771
+ throw new Error(`multiple operations target ${patchPath}`);
150772
+ }
150773
+ seen.add(resolved);
150774
+ }
150775
+ }
150764
150776
  async function deriveNewContentsFromChunks(absolutePath, filePathForErrors, chunks) {
150765
150777
  let originalContents = "";
150766
150778
  try {
@@ -182861,7 +182873,71 @@ function rebuildInputWithFreshDenials(currentInput, serverApprovals, denialReaso
182861
182873
  function shouldAttemptApprovalRecovery(opts) {
182862
182874
  return opts.approvalPendingDetected && opts.retries < opts.maxRetries;
182863
182875
  }
182864
- var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENTS, CONVERSATION_BUSY_RUN_ID_PATTERN, EMPTY_RESPONSE_DETAIL_FRAGMENT = "empty content in", RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_RUN_ERROR_TYPES, NON_RETRYABLE_429_REASONS, NON_RETRYABLE_QUOTA_DETAIL_PATTERNS, NON_RETRYABLE_4XX_PATTERN, RETRYABLE_429_PATTERN, DEFAULT_TRANSIENT_RETRY_BASE_DELAY_MS = 1000, CLOUDFLARE_EDGE_52X_RETRY_BASE_DELAY_MS = 5000, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 1e4, EMPTY_RESPONSE_RETRY_BASE_DELAY_MS = 500, STALE_APPROVAL_RECOVERY_DENIAL_REASON = "The agent harness automatically closed this stale pending tool call to recover from a client/server state desync: the server was still waiting on a result for it, but the harness had no matching tool execution in flight and no result was ever recorded. It was not denied by the user or a permissions policy. Re-issue the tool call if you still need it.";
182876
+ function parseChatGPTUsageLimitDetail(detail) {
182877
+ if (typeof detail !== "string")
182878
+ return null;
182879
+ if (!detail.toLowerCase().includes(CHATGPT_USAGE_LIMIT_FRAGMENT)) {
182880
+ return null;
182881
+ }
182882
+ const fallback = { planType: null, resetsAt: null };
182883
+ const jsonStart = detail.indexOf("{");
182884
+ const jsonEnd = detail.lastIndexOf("}");
182885
+ if (jsonStart === -1 || jsonEnd <= jsonStart)
182886
+ return fallback;
182887
+ let parsed;
182888
+ try {
182889
+ parsed = JSON.parse(detail.slice(jsonStart, jsonEnd + 1));
182890
+ } catch {
182891
+ return fallback;
182892
+ }
182893
+ if (typeof parsed !== "object" || parsed === null)
182894
+ return fallback;
182895
+ const errorField = parsed.error;
182896
+ const errorObj = typeof errorField === "object" && errorField !== null ? errorField : parsed;
182897
+ const planType = typeof errorObj.plan_type === "string" && errorObj.plan_type.length > 0 ? errorObj.plan_type : null;
182898
+ let resetsAt = null;
182899
+ if (typeof errorObj.resets_at === "number" && errorObj.resets_at > 0) {
182900
+ resetsAt = errorObj.resets_at * 1000;
182901
+ } else if (typeof errorObj.resets_in_seconds === "number" && errorObj.resets_in_seconds > 0) {
182902
+ resetsAt = Date.now() + errorObj.resets_in_seconds * 1000;
182903
+ }
182904
+ return { planType, resetsAt };
182905
+ }
182906
+ function isChatGPTByokModel(model) {
182907
+ return model.providerType === CHATGPT_OAUTH_PROVIDER_TYPE2 && model.providerCategory === BYOK_PROVIDER_CATEGORY;
182908
+ }
182909
+ function selectChatGPTQuotaFailoverHandle(params) {
182910
+ const { currentHandle, models: models3, exhaustedProviders } = params;
182911
+ const random = params.random ?? Math.random;
182912
+ const slashIndex = currentHandle.indexOf("/");
182913
+ if (slashIndex <= 0)
182914
+ return null;
182915
+ const currentProvider = currentHandle.slice(0, slashIndex);
182916
+ const modelSuffix = currentHandle.slice(slashIndex + 1);
182917
+ if (!modelSuffix)
182918
+ return null;
182919
+ const currentEntry = models3.find((m2) => m2.handle === currentHandle);
182920
+ if (!currentEntry || !isChatGPTByokModel(currentEntry))
182921
+ return null;
182922
+ const candidates2 = models3.filter((m2) => {
182923
+ if (!isChatGPTByokModel(m2))
182924
+ return false;
182925
+ const idx = m2.handle.indexOf("/");
182926
+ if (idx <= 0)
182927
+ return false;
182928
+ const provider = m2.handle.slice(0, idx);
182929
+ if (provider === currentProvider)
182930
+ return false;
182931
+ if (exhaustedProviders.has(provider))
182932
+ return false;
182933
+ return m2.handle.slice(idx + 1) === modelSuffix;
182934
+ });
182935
+ if (candidates2.length === 0)
182936
+ return null;
182937
+ const index = Math.min(Math.floor(random() * candidates2.length), candidates2.length - 1);
182938
+ return candidates2[index]?.handle ?? null;
182939
+ }
182940
+ var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENTS, CONVERSATION_BUSY_RUN_ID_PATTERN, EMPTY_RESPONSE_DETAIL_FRAGMENT = "empty content in", RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_RUN_ERROR_TYPES, NON_RETRYABLE_429_REASONS, NON_RETRYABLE_QUOTA_DETAIL_PATTERNS, NON_RETRYABLE_4XX_PATTERN, RETRYABLE_429_PATTERN, DEFAULT_TRANSIENT_RETRY_BASE_DELAY_MS = 1000, CLOUDFLARE_EDGE_52X_RETRY_BASE_DELAY_MS = 5000, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 1e4, EMPTY_RESPONSE_RETRY_BASE_DELAY_MS = 500, STALE_APPROVAL_RECOVERY_DENIAL_REASON = "The agent harness automatically closed this stale pending tool call to recover from a client/server state desync: the server was still waiting on a result for it, but the harness had no matching tool execution in flight and no result was ever recorded. It was not denied by the user or a permissions policy. Re-issue the tool call if you still need it.", CHATGPT_USAGE_LIMIT_FRAGMENT = "usage_limit_reached", CHATGPT_OAUTH_PROVIDER_TYPE2 = "chatgpt_oauth", BYOK_PROVIDER_CATEGORY = "byok";
182865
182941
  var init_turn_recovery_policy = __esm(() => {
182866
182942
  init_error_formatter();
182867
182943
  init_zai_errors();
@@ -457811,6 +457887,96 @@ var init_approval_recovery = __esm(() => {
457811
457887
  init_turn_recovery_policy();
457812
457888
  });
457813
457889
 
457890
+ // src/agent/chatgpt-plan-rotation.ts
457891
+ function providerFromHandle(handle2) {
457892
+ const idx = handle2.indexOf("/");
457893
+ return idx > 0 ? handle2.slice(0, idx) : null;
457894
+ }
457895
+ function isChatGPTByokHandleInModels(handle2, models3) {
457896
+ const entry = models3.find((m4) => m4.handle === handle2);
457897
+ return entry?.providerType === "chatgpt_oauth" && entry?.providerCategory === "byok";
457898
+ }
457899
+ async function resolveAgentModelHandle2(agentId) {
457900
+ try {
457901
+ const agent2 = await getBackend().retrieveAgent(agentId);
457902
+ const record5 = agent2;
457903
+ if (typeof record5.model === "string" && record5.model.length > 0) {
457904
+ return record5.model;
457905
+ }
457906
+ return resolveModelHandleFromLlmConfig(record5.llm_config);
457907
+ } catch {
457908
+ return null;
457909
+ }
457910
+ }
457911
+ async function rotateChatGPTPlanOnQuotaLimit(params) {
457912
+ const { agentId, detail } = params;
457913
+ const parsedDetail = parseChatGPTUsageLimitDetail(detail);
457914
+ if (!parsedDetail)
457915
+ return null;
457916
+ let models3 = getCachedAvailableModels();
457917
+ if (!models3) {
457918
+ try {
457919
+ await getAvailableModelHandles();
457920
+ models3 = getCachedAvailableModels();
457921
+ } catch {
457922
+ return null;
457923
+ }
457924
+ }
457925
+ if (!models3)
457926
+ return null;
457927
+ let currentHandle = params.currentHandle;
457928
+ if (!currentHandle || !isChatGPTByokHandleInModels(currentHandle, models3)) {
457929
+ currentHandle = await resolveAgentModelHandle2(agentId);
457930
+ }
457931
+ if (!currentHandle || !isChatGPTByokHandleInModels(currentHandle, models3)) {
457932
+ return null;
457933
+ }
457934
+ const fromProvider = providerFromHandle(currentHandle);
457935
+ if (!fromProvider)
457936
+ return null;
457937
+ exhaustedProviders.add(fromProvider);
457938
+ const toHandle = selectChatGPTQuotaFailoverHandle({
457939
+ currentHandle,
457940
+ models: models3,
457941
+ exhaustedProviders
457942
+ });
457943
+ if (!toHandle)
457944
+ return null;
457945
+ const toProvider = providerFromHandle(toHandle);
457946
+ if (!toProvider)
457947
+ return null;
457948
+ try {
457949
+ await updateAgentLLMConfig(agentId, toHandle, {
457950
+ provider_type: "chatgpt_oauth"
457951
+ });
457952
+ } catch {
457953
+ return null;
457954
+ }
457955
+ return {
457956
+ fromProvider,
457957
+ toProvider,
457958
+ toHandle,
457959
+ resetsAt: parsedDetail.resetsAt
457960
+ };
457961
+ }
457962
+ function formatPlanRotationNotice(params) {
457963
+ const { fromProvider, toProvider, resetsAt } = params;
457964
+ const resetSuffix = resetsAt !== null ? ` (resets ${new Date(resetsAt).toLocaleTimeString([], {
457965
+ hour: "numeric",
457966
+ minute: "2-digit"
457967
+ })})` : "";
457968
+ return `${fromProvider} hit its usage limit${resetSuffix} — switched to ${toProvider}`;
457969
+ }
457970
+ var CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN = 3, exhaustedProviders;
457971
+ var init_chatgpt_plan_rotation = __esm(() => {
457972
+ init_available_models();
457973
+ init_model_handles();
457974
+ init_modify();
457975
+ init_turn_recovery_policy();
457976
+ init_backend2();
457977
+ exhaustedProviders = new Set;
457978
+ });
457979
+
457814
457980
  // src/websocket/listener/skill-injection.ts
457815
457981
  function injectQueuedSkillContent(messages, context3) {
457816
457982
  const skillContents = consumeQueuedSkillContent();
@@ -462811,7 +462977,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
462811
462977
  const normalizedAgentId = normalizeCwdAgentId(agentId);
462812
462978
  const turnWorkingDirectory = getConversationWorkingDirectory(runtime.listener, normalizedAgentId, conversationId);
462813
462979
  const turnPermissionModeState = getOrCreateConversationPermissionModeStateRef(runtime.listener, normalizedAgentId, conversationId);
462814
- let postStopApprovalRecoveryRetries = 0, llmApiErrorRetries = 0, emptyResponseRetries = 0, lastApprovalContinuationAccepted = false, activeDequeuedBatchId = dequeuedBatchId;
462980
+ let postStopApprovalRecoveryRetries = 0, llmApiErrorRetries = 0, emptyResponseRetries = 0, chatgptPlanSwaps = 0, lastApprovalContinuationAccepted = false, activeDequeuedBatchId = dequeuedBatchId;
462815
462981
  const msgRunIds = [];
462816
462982
  let lastExecutionResults = null;
462817
462983
  let lastExecutingToolCallIds = [];
@@ -463181,6 +463347,51 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
463181
463347
  turnToolContextId = getStreamToolContextId(stream12);
463182
463348
  continue;
463183
463349
  }
463350
+ if (chatgptPlanSwaps < CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN) {
463351
+ const rotation = await rotateChatGPTPlanOnQuotaLimit({
463352
+ agentId,
463353
+ currentHandle: null,
463354
+ detail: errorDetail2
463355
+ });
463356
+ if (rotation) {
463357
+ chatgptPlanSwaps += 1;
463358
+ emitRecoverableRetryNotice(socket, runtime, {
463359
+ kind: "transient_provider_retry",
463360
+ message: formatPlanRotationNotice(rotation),
463361
+ reason: "llm_api_error",
463362
+ attempt: chatgptPlanSwaps,
463363
+ maxAttempts: CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN,
463364
+ delayMs: 0,
463365
+ runId: lastRunId || undefined,
463366
+ agentId,
463367
+ conversationId
463368
+ });
463369
+ if (turnAbortSignal.aborted) {
463370
+ throw new Error("Cancelled by user");
463371
+ }
463372
+ turnInput = refreshTurnInputOtidsForNewRequest(turnInput);
463373
+ setTurnLoopStatus(runtime, turnLease, "SENDING_API_REQUEST", {
463374
+ agent_id: agentId,
463375
+ conversation_id: conversationId
463376
+ });
463377
+ const retryInputWithSkillContent = injectQueuedSkillContent(turnInput.messages, { socket, runtime, agentId, conversationId });
463378
+ const retrySendResult = await turnInputSender.send(retryInputWithSkillContent);
463379
+ turnInput = updateTurnInputMessagesPreservingOtids(turnInput, retryInputWithSkillContent);
463380
+ const retryStream = turnInputSender.accept(retrySendResult);
463381
+ if (!retryStream) {
463382
+ return;
463383
+ }
463384
+ stream12 = retryStream;
463385
+ pendingNormalizationInterruptedToolCallIds = [];
463386
+ markAwaitingAcceptedApprovalContinuationRunId(runtime, turnLease, turnInput.messages);
463387
+ setTurnLoopStatus(runtime, turnLease, "PROCESSING_API_RESPONSE", {
463388
+ agent_id: agentId,
463389
+ conversation_id: conversationId
463390
+ });
463391
+ turnToolContextId = getStreamToolContextId(stream12);
463392
+ continue;
463393
+ }
463394
+ }
463184
463395
  const retriable = await isRetriablePostStopError(stopReason || "error", lastRunId, errorDetail2);
463185
463396
  if (finishIfInterrupted(lastRunId || runtime.activeRunId)) {
463186
463397
  break;
@@ -463464,6 +463675,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
463464
463675
  }
463465
463676
  var init_turn = __esm(async () => {
463466
463677
  init_approval_recovery();
463678
+ init_chatgpt_plan_rotation();
463467
463679
  init_check_approval();
463468
463680
  init_turn_recovery_policy();
463469
463681
  init_backend2();
@@ -485848,6 +486060,9 @@ function isAssistantMessage2(message) {
485848
486060
  function isUserMessage(message) {
485849
486061
  return message.message_type === "user_message";
485850
486062
  }
486063
+ function isTaskNotificationMessage(message) {
486064
+ return extractMessageText(message).trimStart().startsWith("<task-notification>");
486065
+ }
485851
486066
  function messageRunId(message) {
485852
486067
  if (!message)
485853
486068
  return null;
@@ -485884,8 +486099,9 @@ async function waitForEnvironmentAssistantMessage(params) {
485884
486099
  return sequenceId !== null && sequenceId > anchorSequenceId;
485885
486100
  });
485886
486101
  const nextUserSequenceId = newerMessages.reduce((closest, message) => {
485887
- if (!isUserMessage(message))
486102
+ if (!isUserMessage(message) || isTaskNotificationMessage(message)) {
485888
486103
  return closest;
486104
+ }
485889
486105
  const sequenceId = messageSequenceId(message);
485890
486106
  if (sequenceId === null)
485891
486107
  return closest;
@@ -497557,7 +497773,7 @@ var init_mcp_client = __esm(() => {
497557
497773
  init_streamableHttp();
497558
497774
  DEFAULT_CLIENT_INFO = {
497559
497775
  name: "letta-code",
497560
- version: "0.30.26"
497776
+ version: "0.30.27"
497561
497777
  };
497562
497778
  });
497563
497779
 
@@ -499949,6 +500165,7 @@ ${loadedContents.join(`
499949
500165
  let llmApiErrorRetries = 0;
499950
500166
  let emptyResponseRetries = 0;
499951
500167
  let conversationBusyRetries = 0;
500168
+ let chatgptPlanSwaps = 0;
499952
500169
  markMilestone("HEADLESS_FIRST_STREAM_START");
499953
500170
  measureSinceMilestone("headless-setup-total", "HEADLESS_CLIENT_READY");
499954
500171
  const checkMaxTurns = async () => {
@@ -500215,6 +500432,7 @@ ${loadedContents.join(`
500215
500432
  llmApiErrorRetries = 0;
500216
500433
  emptyResponseRetries = 0;
500217
500434
  conversationBusyRetries = 0;
500435
+ chatgptPlanSwaps = 0;
500218
500436
  const continueMessage = await emitHeadlessTurnEnd({
500219
500437
  agent: agent2,
500220
500438
  conversationId,
@@ -500315,6 +500533,34 @@ ${loadedContents.join(`
500315
500533
  }
500316
500534
  }
500317
500535
  const detailFromRun = await fetchRunErrorDetail(lastRunId);
500536
+ if (chatgptPlanSwaps < CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN) {
500537
+ const rotation = await rotateChatGPTPlanOnQuotaLimit({
500538
+ agentId: agent2.id,
500539
+ currentHandle: null,
500540
+ detail: detailFromRun ?? latestErrorText
500541
+ });
500542
+ if (rotation) {
500543
+ chatgptPlanSwaps += 1;
500544
+ const rotationMessage = formatPlanRotationNotice(rotation);
500545
+ if (outputFormat === "stream-json") {
500546
+ const retryMsg = {
500547
+ type: "retry",
500548
+ reason: "llm_api_error",
500549
+ attempt: chatgptPlanSwaps,
500550
+ max_attempts: CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN,
500551
+ delay_ms: 0,
500552
+ run_id: lastRunId ?? undefined,
500553
+ session_id: sessionId,
500554
+ uuid: `retry-${lastRunId || randomUUID35()}`
500555
+ };
500556
+ writeWireMessage(retryMsg);
500557
+ } else {
500558
+ console.error(rotationMessage);
500559
+ }
500560
+ currentInput = refreshInputOtidsForNewRequest(currentInput);
500561
+ continue;
500562
+ }
500563
+ }
500318
500564
  if (stopReason === "llm_api_error") {
500319
500565
  if (llmApiErrorRetries < LLM_API_ERROR_MAX_RETRIES2) {
500320
500566
  const attempt2 = llmApiErrorRetries + 1;
@@ -501686,6 +501932,7 @@ var init_headless = __esm(async () => {
501686
501932
  init_subagent_stdout_failure();
501687
501933
  init_approval_recovery();
501688
501934
  init_bootstrap_handler();
501935
+ init_chatgpt_plan_rotation();
501689
501936
  init_client_skills();
501690
501937
  init_context();
501691
501938
  init_create5();
@@ -547536,6 +547783,7 @@ function useConversationLoop(ctx) {
547536
547783
  buffersRef,
547537
547784
  clearApprovalToolContext,
547538
547785
  closeTrajectorySegment,
547786
+ chatgptPlanSwapsRef,
547539
547787
  consumeQueuedMessages,
547540
547788
  queueModeRef,
547541
547789
  contextTrackerRef,
@@ -547767,6 +548015,7 @@ function useConversationLoop(ctx) {
547767
548015
  emptyResponseRetriesRef.current = 0;
547768
548016
  conversationBusyRetriesRef.current = 0;
547769
548017
  quotaAutoSwapAttemptedRef.current = false;
548018
+ chatgptPlanSwapsRef.current = 0;
547770
548019
  }
547771
548020
  let currentRunId;
547772
548021
  let preserveTranscriptStartForApproval = false;
@@ -548731,6 +548980,27 @@ ${feedback}
548731
548980
  buffersRef.current.interrupted = false;
548732
548981
  continue;
548733
548982
  }
548983
+ if (chatgptPlanSwapsRef.current < CHATGPT_PLAN_ROTATION_MAX_SWAPS_PER_TURN) {
548984
+ const rotation = await rotateChatGPTPlanOnQuotaLimit({
548985
+ agentId: agentIdRef.current,
548986
+ currentHandle: currentModelId,
548987
+ detail: detailFromRun ?? fallbackError
548988
+ });
548989
+ if (rotation) {
548990
+ chatgptPlanSwapsRef.current += 1;
548991
+ const statusId = uid("status");
548992
+ buffersRef.current.byId.set(statusId, {
548993
+ kind: "status",
548994
+ id: statusId,
548995
+ lines: [formatPlanRotationNotice(rotation)]
548996
+ });
548997
+ buffersRef.current.order.push(statusId);
548998
+ refreshDerived();
548999
+ currentInput = refreshInputOtidsForNewRequest(currentInput);
549000
+ buffersRef.current.interrupted = false;
549001
+ continue;
549002
+ }
549003
+ }
548734
549004
  const autoSwapOnQuotaLimitEnabled = settingsManager.getSetting("autoSwapOnQuotaLimit") !== false;
548735
549005
  const supportsHostedAutoQuotaFallback = !getBackend().capabilities.localModelCatalog;
548736
549006
  const isQuotaLimit = isQuotaLimitErrorDetail(detailFromRun ?? fallbackError);
@@ -549012,6 +549282,7 @@ var init_use_conversation_loop = __esm(async () => {
549012
549282
  init_error();
549013
549283
  init_approval_recovery();
549014
549284
  init_available_models();
549285
+ init_chatgpt_plan_rotation();
549015
549286
  init_check_approval();
549016
549287
  init_model();
549017
549288
  init_prompt_assets();
@@ -557857,6 +558128,7 @@ function App2({
557857
558128
  const llmApiErrorRetriesRef = import_react121.useRef(0);
557858
558129
  const quotaAutoSwapAttemptedRef = import_react121.useRef(false);
557859
558130
  const emptyResponseRetriesRef = import_react121.useRef(0);
558131
+ const chatgptPlanSwapsRef = import_react121.useRef(0);
557860
558132
  const conversationBusyRetriesRef = import_react121.useRef(0);
557861
558133
  const [queueDisplay, setQueueDisplay] = import_react121.useState([]);
557862
558134
  const tuiQueueRef = import_react121.useRef(null);
@@ -559345,6 +559617,7 @@ Memory may be stale. Try running: git -C ${getScopedMemoryFilesystemRoot(agentId
559345
559617
  autoAllowedExecutionRef,
559346
559618
  buffersRef,
559347
559619
  clearApprovalToolContext,
559620
+ chatgptPlanSwapsRef,
559348
559621
  closeTrajectorySegment,
559349
559622
  consumeQueuedMessages,
559350
559623
  queueModeRef,
@@ -564456,4 +564729,4 @@ function registerBunOAuthFlows() {
564456
564729
  registerBunOAuthFlows();
564457
564730
  await init_src5().then(() => exports_src2);
564458
564731
 
564459
- //# debugId=BBC1C9C009AB28B764756E2164756E21
564732
+ //# debugId=995B0F0DB5D2C58C64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.30.26",
3
+ "version": "0.30.27",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.10",
@@ -5,11 +5,11 @@
5
5
  "src/backend/local/local-backend.ts": 1014,
6
6
  "src/backend/local/local-store.ts": 3459,
7
7
  "src/backend/pi-stream-adapter.test.ts": 1304,
8
- "src/cli/app/AppCoordinator.tsx": 5186,
8
+ "src/cli/app/AppCoordinator.tsx": 5189,
9
9
  "src/cli/app/AppView.tsx": 1735,
10
10
  "src/cli/app/use-approval-flow.ts": 1163,
11
11
  "src/cli/app/use-configuration-handlers.ts": 1421,
12
- "src/cli/app/use-conversation-loop.ts": 2871,
12
+ "src/cli/app/use-conversation-loop.ts": 2912,
13
13
  "src/cli/app/use-submit-handler.ts": 4077,
14
14
  "src/cli/components/AgentSelector.tsx": 1104,
15
15
  "src/cli/components/InputRich.tsx": 2225,
@@ -21,7 +21,7 @@
21
21
  "src/cli/mods/local-mod-loader.test.ts": 1043,
22
22
  "src/cli/reflection-transcript.test.ts": 1084,
23
23
  "src/cli/subcommands/skills.ts": 1264,
24
- "src/headless.ts": 4953,
24
+ "src/headless.ts": 4994,
25
25
  "src/hooks/integration.test.ts": 1147,
26
26
  "src/index.ts": 2775,
27
27
  "src/mods/learning-harness.ts": 2434,
@@ -44,5 +44,6 @@
44
44
  "src/websocket/listener/file-commands.ts": 1053,
45
45
  "src/websocket/listener/lifecycle.ts": 1048,
46
46
  "src/websocket/listener/protocol-inbound.ts": 2242,
47
- "src/websocket/listener/protocol-outbound.ts": 1070
47
+ "src/websocket/listener/protocol-outbound.ts": 1070,
48
+ "src/websocket/listener/turn.ts": 1058
48
49
  }