@letta-ai/letta-code 0.32.2 → 0.32.3
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/dist/channels-public.js +32 -2
- package/dist/channels-public.js.map +3 -3
- package/dist/channels-slack.js +52 -2
- package/dist/channels-slack.js.map +4 -4
- package/dist/gateway-core.js +89 -31
- package/dist/gateway-core.js.map +6 -5
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/skills.d.ts.map +1 -1
- package/dist/types/channels/gateway-core.d.ts +2 -0
- package/dist/types/channels/gateway-core.d.ts.map +1 -1
- package/dist/types/channels/gateway-sources.d.ts +6 -0
- package/dist/types/channels/gateway-sources.d.ts.map +1 -0
- package/dist/types/channels/progress-builder.d.ts +1 -1
- package/dist/types/channels/progress-builder.d.ts.map +1 -1
- package/dist/types/channels/progress-types.d.ts +28 -0
- package/dist/types/channels/progress-types.d.ts.map +1 -0
- package/dist/types/channels/slack/progress.d.ts.map +1 -1
- package/dist/types/channels/slack/status-controller.d.ts +2 -1
- package/dist/types/channels/slack/status-controller.d.ts.map +1 -1
- package/dist/types/channels/types.d.ts +6 -23
- package/dist/types/channels/types.d.ts.map +1 -1
- package/letta.js +234 -64
- package/package.json +1 -1
- package/skills/image-generation/SKILL.md +5 -4
- package/skills/working-across-computers/SKILL.md +88 -0
- package/skills/teleporting-between-environments/SKILL.md +0 -114
package/letta.js
CHANGED
|
@@ -5515,7 +5515,7 @@ var package_default;
|
|
|
5515
5515
|
var init_package = __esm(() => {
|
|
5516
5516
|
package_default = {
|
|
5517
5517
|
name: "@letta-ai/letta-code",
|
|
5518
|
-
version: "0.32.
|
|
5518
|
+
version: "0.32.3",
|
|
5519
5519
|
lettaStartupLogProtocol: 1,
|
|
5520
5520
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
5521
5521
|
type: "module",
|
|
@@ -98016,7 +98016,7 @@ Note: \`fork\` cannot be combined with \`agent_id\` or \`conversation_id\`.
|
|
|
98016
98016
|
|
|
98017
98017
|
## Running on Another Computer
|
|
98018
98018
|
|
|
98019
|
-
Pass \`computer\` to run the subagent's turn on another connected computer instead of this machine. Works with any subagent type. The call fails fast if the named device is offline,
|
|
98019
|
+
Pass \`computer\` to run the subagent's turn on another connected computer instead of this machine. Prefer a stable device ID or computer name; these select the freshest online listener for that device. Ephemeral connection IDs are still supported to pin a specific listener. Works with any subagent type. The call fails fast if the named device is offline, the name matches multiple online devices, or the listener is too old to support routing.
|
|
98020
98020
|
|
|
98021
98021
|
\`computer: "cloud"\` provisions a Cloud sandbox for the subagent's conversation and runs the turn there. Sandboxes are per-conversation: this is a separate machine from wherever you are running now, even if you are already in a Cloud sandbox.
|
|
98022
98022
|
|
|
@@ -122477,7 +122477,8 @@ var init_skills5 = __esm(() => {
|
|
|
122477
122477
|
init_skill_sources();
|
|
122478
122478
|
LOCAL_AGENT_EXCLUDED_BUNDLED_SKILLS = new Set([
|
|
122479
122479
|
"image-generation",
|
|
122480
|
-
"managing-shared-memory"
|
|
122480
|
+
"managing-shared-memory",
|
|
122481
|
+
"working-across-computers"
|
|
122481
122482
|
]);
|
|
122482
122483
|
PROJECT_SKILLS_DIR = join26(".agents", "skills");
|
|
122483
122484
|
GLOBAL_SKILLS_DIR = join26(process.env.HOME || process.env.USERPROFILE || "~", ".letta/skills");
|
|
@@ -126332,7 +126333,7 @@ var init_Task2 = __esm(() => {
|
|
|
126332
126333
|
},
|
|
126333
126334
|
computer: {
|
|
126334
126335
|
type: "string",
|
|
126335
|
-
description: `Run the subagent on another connected computer instead of this machine.
|
|
126336
|
+
description: `Run the subagent on another connected computer instead of this machine. Prefer a stable device ID or computer name; ephemeral connection IDs are also supported to pin a specific listener. Pass "cloud" to provision a Cloud sandbox for the subagent's conversation. Fails fast if the device is offline or does not support routing. Omit this field to run on the current machine (the default) — only set it when the task specifically needs to run elsewhere.`
|
|
126336
126337
|
}
|
|
126337
126338
|
},
|
|
126338
126339
|
required: ["description", "prompt", "subagent_type"],
|
|
@@ -192396,7 +192397,8 @@ function resolveSlackConcreteActivity(event) {
|
|
|
192396
192397
|
if (event.kind !== "tool" || !isNonEmptyString5(event.toolName) || event.toolName.toLowerCase() === "messagechannel") {
|
|
192397
192398
|
return null;
|
|
192398
192399
|
}
|
|
192399
|
-
|
|
192400
|
+
const descriptions = event.toolBatchTitle !== undefined ? [event.toolBatchTitle] : [event.toolTitle, event.toolDetails];
|
|
192401
|
+
for (const description of descriptions) {
|
|
192400
192402
|
if (!isNonEmptyString5(description)) {
|
|
192401
192403
|
continue;
|
|
192402
192404
|
}
|
|
@@ -195267,6 +195269,12 @@ ${loadingText}`;
|
|
|
195267
195269
|
signatureByConversation.set(stateKey, signature);
|
|
195268
195270
|
const previous = writePromiseByConversation.get(stateKey) ?? Promise.resolve();
|
|
195269
195271
|
const operation = previous.then(async () => {
|
|
195272
|
+
if (footerText && !stateByConversation.get(stateKey)?.isThinkingActive) {
|
|
195273
|
+
if (signatureByConversation.get(stateKey) === signature) {
|
|
195274
|
+
signatureByConversation.delete(stateKey);
|
|
195275
|
+
}
|
|
195276
|
+
return false;
|
|
195277
|
+
}
|
|
195270
195278
|
try {
|
|
195271
195279
|
await setStatus.call(slackClient.assistant?.threads, {
|
|
195272
195280
|
channel_id: source.chatId,
|
|
@@ -195274,6 +195282,16 @@ ${loadingText}`;
|
|
|
195274
195282
|
status: footerText,
|
|
195275
195283
|
...footerText ? { loading_messages: [loadingText] } : {}
|
|
195276
195284
|
});
|
|
195285
|
+
const state = stateByConversation.get(stateKey);
|
|
195286
|
+
if (footerText && state && !state.isThinkingActive) {
|
|
195287
|
+
await setStatus.call(slackClient.assistant?.threads, {
|
|
195288
|
+
channel_id: source.chatId,
|
|
195289
|
+
thread_ts: threadTs,
|
|
195290
|
+
status: ""
|
|
195291
|
+
});
|
|
195292
|
+
clearedStaleReplyKeys.add(replyKey);
|
|
195293
|
+
return true;
|
|
195294
|
+
}
|
|
195277
195295
|
if (footerText)
|
|
195278
195296
|
clearedStaleReplyKeys.delete(replyKey);
|
|
195279
195297
|
else
|
|
@@ -195372,7 +195390,40 @@ ${loadingText}`;
|
|
|
195372
195390
|
await writeStatus(source, "", "", { force: true });
|
|
195373
195391
|
signatureByConversation.delete(key2);
|
|
195374
195392
|
}
|
|
195393
|
+
async function refresh(source) {
|
|
195394
|
+
const key2 = getConversationKey(source);
|
|
195395
|
+
const state = key2 ? stateByConversation.get(key2) : undefined;
|
|
195396
|
+
if (!key2 || !state?.isThinkingActive)
|
|
195397
|
+
return false;
|
|
195398
|
+
await writeStatus(source, state.typingFooterText, state.thinkingText, {
|
|
195399
|
+
force: true
|
|
195400
|
+
});
|
|
195401
|
+
if (stateByConversation.get(key2) === state && state.isThinkingActive) {
|
|
195402
|
+
scheduleKeepalive(key2);
|
|
195403
|
+
}
|
|
195404
|
+
return true;
|
|
195405
|
+
}
|
|
195406
|
+
async function handleLifecycle(event) {
|
|
195407
|
+
if (event.type === "queued") {
|
|
195408
|
+
if (await refresh(event.source))
|
|
195409
|
+
return;
|
|
195410
|
+
if (event.source.showStartupStatus) {
|
|
195411
|
+
await activate(event.source, SLACK_ASSISTANT_STARTUP_STATUS, SLACK_ASSISTANT_STARTUP_STATUS);
|
|
195412
|
+
}
|
|
195413
|
+
return;
|
|
195414
|
+
}
|
|
195415
|
+
for (const source of getUniqueSources(event.sources)) {
|
|
195416
|
+
if (event.type === "processing") {
|
|
195417
|
+
await clearStale(source);
|
|
195418
|
+
} else if (event.stopReason !== "requires_approval") {
|
|
195419
|
+
const remaining = event.remainingSources?.some((other) => other.accountId === source.accountId && getConversationKey(other) === getConversationKey(source) && getLifecycleReplyKey(other) === getLifecycleReplyKey(source));
|
|
195420
|
+
if (!remaining)
|
|
195421
|
+
await deactivate(source);
|
|
195422
|
+
}
|
|
195423
|
+
}
|
|
195424
|
+
}
|
|
195375
195425
|
return {
|
|
195426
|
+
handleLifecycle,
|
|
195376
195427
|
getUniqueSources,
|
|
195377
195428
|
getLifecycleErrorReplyKey,
|
|
195378
195429
|
activate,
|
|
@@ -195412,7 +195463,9 @@ ${loadingText}`;
|
|
|
195412
195463
|
};
|
|
195413
195464
|
}
|
|
195414
195465
|
var SLACK_ASSISTANT_STATUS_KEEPALIVE_MS = 90000;
|
|
195415
|
-
var init_status_controller = () => {
|
|
195466
|
+
var init_status_controller = __esm(() => {
|
|
195467
|
+
init_progress();
|
|
195468
|
+
});
|
|
195416
195469
|
|
|
195417
195470
|
// src/channels/slack/thread-context.ts
|
|
195418
195471
|
function shouldHydrateCurrentSlackMessageAttachments(msg) {
|
|
@@ -195695,19 +195748,19 @@ function createSlackAdapter(config) {
|
|
|
195695
195748
|
if (!running)
|
|
195696
195749
|
return;
|
|
195697
195750
|
if (event.type === "queued") {
|
|
195698
|
-
|
|
195699
|
-
|
|
195700
|
-
|
|
195751
|
+
const showStartupStatus = event.source.showStartupStatus || isSlackFlatChannelThreadOpener(event.source) && isNonEmptyString4(event.source.messageId) && !agentThreadTracker.has(event.source.chatId, event.source.messageId);
|
|
195752
|
+
await status.handleLifecycle({
|
|
195753
|
+
...event,
|
|
195754
|
+
source: { ...event.source, showStartupStatus }
|
|
195755
|
+
});
|
|
195701
195756
|
return;
|
|
195702
195757
|
}
|
|
195703
195758
|
const sources = status.getUniqueSources(event.sources);
|
|
195704
|
-
|
|
195705
|
-
|
|
195759
|
+
await status.handleLifecycle(event);
|
|
195760
|
+
if (event.type === "processing")
|
|
195706
195761
|
return;
|
|
195707
|
-
}
|
|
195708
195762
|
if (event.stopReason === "requires_approval")
|
|
195709
195763
|
return;
|
|
195710
|
-
await Promise.all(sources.map(status.deactivate));
|
|
195711
195764
|
if (!shouldPostSlackTerminalError(event.stopReason))
|
|
195712
195765
|
return;
|
|
195713
195766
|
const errorText = event.error?.trim() ?? "";
|
|
@@ -217299,7 +217352,7 @@ function shouldProcessInboundMessageDirectly(runtime, parsed) {
|
|
|
217299
217352
|
});
|
|
217300
217353
|
return getListenerBlockedReason(runtime.turnLifecycle.snapshot(), activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0) === null;
|
|
217301
217354
|
}
|
|
217302
|
-
function consumeQueuedTurn(runtime) {
|
|
217355
|
+
function consumeQueuedTurn(runtime, continuation) {
|
|
217303
217356
|
const queuedItems = runtime.queueRuntime.peekReady();
|
|
217304
217357
|
const firstQueuedItem = queuedItems[0];
|
|
217305
217358
|
if (!firstQueuedItem || !isCoalescable(firstQueuedItem.kind)) {
|
|
@@ -217315,6 +217368,9 @@ function consumeQueuedTurn(runtime) {
|
|
|
217315
217368
|
let batchImageFailureMode = null;
|
|
217316
217369
|
const isNoCoalesce = (candidate) => candidate.kind === "message" && candidate.noCoalesce === true;
|
|
217317
217370
|
for (const item of queuedItems) {
|
|
217371
|
+
if (continuation && item.actingUserId !== continuation.actingUserId) {
|
|
217372
|
+
break;
|
|
217373
|
+
}
|
|
217318
217374
|
if (!isCoalescable(item.kind) || !hasSameQueueScope(firstQueuedItem, item)) {
|
|
217319
217375
|
break;
|
|
217320
217376
|
}
|
|
@@ -218202,12 +218258,12 @@ async function resolveDesktopEnvironmentConnectionId(list = listEnvironments) {
|
|
|
218202
218258
|
}
|
|
218203
218259
|
return { connectionId: environment2.connectionId, environment: environment2 };
|
|
218204
218260
|
}
|
|
218205
|
-
async function resolveEnvironmentConnectionId(selector) {
|
|
218261
|
+
async function resolveEnvironmentConnectionId(selector, list = listEnvironments) {
|
|
218206
218262
|
const trimmed = selector.trim();
|
|
218207
218263
|
if (!trimmed) {
|
|
218208
218264
|
throw new Error("Computer selector must not be empty");
|
|
218209
218265
|
}
|
|
218210
|
-
const response = await
|
|
218266
|
+
const response = await list({ limit: 100 });
|
|
218211
218267
|
const matches3 = response.connections.filter((environment3) => {
|
|
218212
218268
|
return environment3.connectionId === trimmed || environment3.id === trimmed || environment3.deviceId === trimmed || environment3.connectionName === trimmed;
|
|
218213
218269
|
});
|
|
@@ -218218,9 +218274,10 @@ async function resolveEnvironmentConnectionId(selector) {
|
|
|
218218
218274
|
if (onlineMatches.length === 0) {
|
|
218219
218275
|
throw new Error(`Computer "${trimmed}" is offline. Matched: ${matches3.map(describeEnvironment).join(", ")}`);
|
|
218220
218276
|
}
|
|
218221
|
-
if (onlineMatches.
|
|
218277
|
+
if (new Set(onlineMatches.map((environment3) => environment3.deviceId)).size > 1) {
|
|
218222
218278
|
throw new Error(`Computer "${trimmed}" is ambiguous. Matched: ${onlineMatches.map(describeEnvironment).join(", ")}`);
|
|
218223
218279
|
}
|
|
218280
|
+
onlineMatches.sort((a2, b3) => Math.max(b3.lastHeartbeat ?? 0, b3.lastSeenAt) - Math.max(a2.lastHeartbeat ?? 0, a2.lastSeenAt));
|
|
218224
218281
|
const environment2 = onlineMatches[0];
|
|
218225
218282
|
if (!environment2) {
|
|
218226
218283
|
throw new Error(`Computer "${trimmed}" is offline`);
|
|
@@ -219150,7 +219207,9 @@ List options:
|
|
|
219150
219207
|
Notes:
|
|
219151
219208
|
- Output is JSON only.
|
|
219152
219209
|
- Uses CLI auth; override with LETTA_API_KEY/LETTA_BASE_URL if needed.
|
|
219153
|
-
- Use letta computers current to get this computer's
|
|
219210
|
+
- Use letta computers current to get this computer's stable deviceId.
|
|
219211
|
+
- Prefer deviceId or connectionName for --computer and Agent(computer=...).
|
|
219212
|
+
connectionId is ephemeral; use it only to pin a specific listener.
|
|
219154
219213
|
- Use --computer cloud to route through the target agent's cloud sandbox.
|
|
219155
219214
|
- Use --computer <name|device-id|connection-id> with headless messaging
|
|
219156
219215
|
to route a message through a specific registered computer.
|
|
@@ -405353,13 +405412,14 @@ async function handleApprovalStop(params) {
|
|
|
405353
405412
|
}
|
|
405354
405413
|
]);
|
|
405355
405414
|
let continuationBatchId = dequeuedBatchId;
|
|
405356
|
-
|
|
405357
|
-
const consumedQueuedTurn = consumeQueuedTurn(runtime
|
|
405415
|
+
const sendOptions = buildSendOptions() ?? {};
|
|
405416
|
+
const consumedQueuedTurn = consumeQueuedTurn(runtime, {
|
|
405417
|
+
actingUserId: sendOptions.actingUserId
|
|
405418
|
+
});
|
|
405358
405419
|
if (consumedQueuedTurn) {
|
|
405359
405420
|
const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
|
|
405360
405421
|
turnCorrelation?.appendDequeuedBatch(dequeuedBatch.batchId);
|
|
405361
405422
|
continuationBatchId = dequeuedBatch.batchId;
|
|
405362
|
-
continuationActingUserId = queuedTurn.actingUserId;
|
|
405363
405423
|
nextTurnInput = appendQueuedTurnToInput(nextTurnInput, queuedTurn);
|
|
405364
405424
|
emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
|
|
405365
405425
|
}
|
|
@@ -405374,11 +405434,9 @@ async function handleApprovalStop(params) {
|
|
|
405374
405434
|
});
|
|
405375
405435
|
let sendResult;
|
|
405376
405436
|
try {
|
|
405377
|
-
const sendOptions = buildSendOptions() ?? {};
|
|
405378
405437
|
const imageFailureModesByMessageOtid = mergeImageFailureModesByMessageOtid(sendOptions.imageFailureModesByMessageOtid, nextTurnInput.imageFailureModesByMessageOtid);
|
|
405379
405438
|
sendResult = await sendApprovalContinuation(conversationId, nextInputWithSkillContent, {
|
|
405380
405439
|
...sendOptions,
|
|
405381
|
-
...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
|
|
405382
405440
|
...imageFailureModesByMessageOtid ? { imageFailureModesByMessageOtid } : {},
|
|
405383
405441
|
...continuationWasFullyAutoHandled ? { allowResponseStateReuse: true } : {}
|
|
405384
405442
|
}, socket, runtime, turnLease);
|
|
@@ -440191,7 +440249,7 @@ var init_mcp_client = __esm(() => {
|
|
|
440191
440249
|
init_streamableHttp();
|
|
440192
440250
|
DEFAULT_CLIENT_INFO = {
|
|
440193
440251
|
name: "letta-code",
|
|
440194
|
-
version: "0.32.
|
|
440252
|
+
version: "0.32.3"
|
|
440195
440253
|
};
|
|
440196
440254
|
});
|
|
440197
440255
|
|
|
@@ -444080,7 +444138,7 @@ async function request(path50, init, deps) {
|
|
|
444080
444138
|
async function ensureConversationSandbox(agentId, conversationId, deps = defaultDeps) {
|
|
444081
444139
|
const response = await request(`/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {
|
|
444082
444140
|
method: "POST",
|
|
444083
|
-
body: JSON.stringify({ conversationId })
|
|
444141
|
+
body: JSON.stringify(conversationId === "default" ? {} : { conversationId })
|
|
444084
444142
|
}, deps);
|
|
444085
444143
|
return await response.json();
|
|
444086
444144
|
}
|
|
@@ -444118,8 +444176,17 @@ Usage:
|
|
|
444118
444176
|
letta sandbox upload <local-path>
|
|
444119
444177
|
letta sandbox download <sandbox-path> [--to <local-path>]
|
|
444120
444178
|
|
|
444179
|
+
Target another conversation (upload or download):
|
|
444180
|
+
letta sandbox upload <local-path> --conversation <conv-id>
|
|
444181
|
+
letta sandbox upload <local-path> --agent <agent-id>
|
|
444182
|
+
|
|
444121
444183
|
Notes:
|
|
444122
|
-
-
|
|
444184
|
+
- Without target flags, uses the active Letta Cloud conversation.
|
|
444185
|
+
- --conversation resolves the owning agent; --agent, if given, must match.
|
|
444186
|
+
- --agent alone selects that agent's main/default sandbox.
|
|
444187
|
+
- --conversation default is also supported and requires explicit --agent.
|
|
444188
|
+
- Target flags override shell/session context without changing it.
|
|
444189
|
+
- Run upload on the computer containing the local file, including a remote subagent.
|
|
444123
444190
|
- Uploads are stored under /root/downloads in the conversation sandbox.
|
|
444124
444191
|
- Downloads are limited to files under /root/downloads.
|
|
444125
444192
|
- Output is JSON only.
|
|
@@ -444156,6 +444223,35 @@ function resolveSandboxSession(env5, fallback) {
|
|
|
444156
444223
|
}
|
|
444157
444224
|
return session2;
|
|
444158
444225
|
}
|
|
444226
|
+
async function resolveSandboxTarget(target2, getCurrentSession, retrieveConversation) {
|
|
444227
|
+
if (target2.agent === undefined && target2.conversation === undefined) {
|
|
444228
|
+
return getCurrentSession();
|
|
444229
|
+
}
|
|
444230
|
+
const agentId = target2.agent?.trim();
|
|
444231
|
+
const conversationId = target2.conversation === undefined && agentId ? "default" : target2.conversation?.trim();
|
|
444232
|
+
if (target2.agent !== undefined && !agentId) {
|
|
444233
|
+
throw new Error("--agent must not be empty");
|
|
444234
|
+
}
|
|
444235
|
+
if (!conversationId || conversationId === "new") {
|
|
444236
|
+
throw new Error("Specify --conversation <conv-id> or --conversation default");
|
|
444237
|
+
}
|
|
444238
|
+
if (agentId && isLocalAgentId(agentId)) {
|
|
444239
|
+
throw new Error("Sandbox file transfer requires a Letta Cloud agent");
|
|
444240
|
+
}
|
|
444241
|
+
if (conversationId === "default") {
|
|
444242
|
+
if (!agentId)
|
|
444243
|
+
throw new Error("--conversation default requires --agent");
|
|
444244
|
+
return { agentId, conversationId };
|
|
444245
|
+
}
|
|
444246
|
+
const conversation = await retrieveConversation(conversationId);
|
|
444247
|
+
if (!conversation.agent_id || isLocalAgentId(conversation.agent_id)) {
|
|
444248
|
+
throw new Error("The target conversation must belong to a Letta Cloud agent");
|
|
444249
|
+
}
|
|
444250
|
+
if (agentId && agentId !== conversation.agent_id) {
|
|
444251
|
+
throw new Error(`Conversation ${conversationId} does not belong to ${agentId}`);
|
|
444252
|
+
}
|
|
444253
|
+
return { agentId: conversation.agent_id, conversationId };
|
|
444254
|
+
}
|
|
444159
444255
|
async function initializeSandboxSettings() {
|
|
444160
444256
|
await settingsManager.initialize();
|
|
444161
444257
|
await settingsManager.loadLocalProjectSettings();
|
|
@@ -444184,24 +444280,37 @@ async function runSandboxSubcommand(argv, deps = {}) {
|
|
|
444184
444280
|
if (!await (deps.isCloud ?? isLettaCloud)()) {
|
|
444185
444281
|
throw new Error("Sandbox file transfer is only available on Letta Cloud");
|
|
444186
444282
|
}
|
|
444187
|
-
const session2 = resolveSandboxSession(process.env, (deps.getLastSession ?? (() => settingsManager.getEffectiveLastSession()))());
|
|
444188
|
-
const
|
|
444283
|
+
const session2 = await resolveSandboxTarget(parsed.values, () => resolveSandboxSession(process.env, (deps.getLastSession ?? (() => settingsManager.getEffectiveLastSession()))()), deps.retrieveConversation ?? (async (id2) => (await getClient()).conversations.retrieve(id2)));
|
|
444284
|
+
const explicitTarget = parsed.values.conversation !== undefined || parsed.values.agent !== undefined;
|
|
444285
|
+
const ensureSandbox = async () => {
|
|
444286
|
+
const sandbox2 = await (deps.ensureSandbox ?? ensureConversationSandbox)(session2.agentId, session2.conversationId);
|
|
444287
|
+
if (explicitTarget && (sandbox2.conversationId ?? "default") !== session2.conversationId) {
|
|
444288
|
+
throw new Error("The server returned a sandbox for a different conversation; no files transferred");
|
|
444289
|
+
}
|
|
444290
|
+
return sandbox2;
|
|
444291
|
+
};
|
|
444292
|
+
const targetOutput = explicitTarget ? session2 : {};
|
|
444189
444293
|
if (action3 === "upload") {
|
|
444190
444294
|
const localPath2 = resolve37(path50);
|
|
444191
444295
|
const fileStat = await (deps.statLocalPath ?? stat17)(localPath2);
|
|
444192
444296
|
if (!fileStat.isFile())
|
|
444193
444297
|
throw new Error(`${localPath2} is not a file`);
|
|
444194
444298
|
const data2 = await (deps.readLocalFile ?? readFile25)(localPath2);
|
|
444195
|
-
const sandbox2 = await ensureSandbox(
|
|
444299
|
+
const sandbox2 = await ensureSandbox();
|
|
444196
444300
|
const result2 = await (deps.uploadFile ?? uploadFileToSandbox)(sandbox2.sandboxId, { blob: new Blob([data2]), name: basename26(localPath2) });
|
|
444197
|
-
console.log(JSON.stringify(result2, null, 2));
|
|
444301
|
+
console.log(JSON.stringify({ ...result2, ...targetOutput }, null, 2));
|
|
444198
444302
|
return 0;
|
|
444199
444303
|
}
|
|
444200
|
-
const sandbox = await ensureSandbox(
|
|
444304
|
+
const sandbox = await ensureSandbox();
|
|
444201
444305
|
const data = await (deps.downloadFile ?? downloadFileFromSandbox)(sandbox.sandboxId, path50);
|
|
444202
444306
|
const localPath = resolve37(parsed.values.to ?? basename26(path50));
|
|
444203
444307
|
await (deps.writeLocalFile ?? writeFile17)(localPath, data);
|
|
444204
|
-
console.log(JSON.stringify({
|
|
444308
|
+
console.log(JSON.stringify({
|
|
444309
|
+
path: localPath,
|
|
444310
|
+
sandboxPath: path50,
|
|
444311
|
+
size: data.byteLength,
|
|
444312
|
+
...targetOutput
|
|
444313
|
+
}, null, 2));
|
|
444205
444314
|
return 0;
|
|
444206
444315
|
} catch (error5) {
|
|
444207
444316
|
console.error(`Error: ${error5 instanceof Error ? error5.message : error5}`);
|
|
@@ -444211,11 +444320,14 @@ async function runSandboxSubcommand(argv, deps = {}) {
|
|
|
444211
444320
|
var SANDBOX_OPTIONS;
|
|
444212
444321
|
var init_sandbox2 = __esm(() => {
|
|
444213
444322
|
init_memory_filesystem2();
|
|
444323
|
+
init_client4();
|
|
444214
444324
|
init_sandbox_files();
|
|
444215
444325
|
init_settings_manager();
|
|
444216
444326
|
SANDBOX_OPTIONS = {
|
|
444217
444327
|
help: { type: "boolean", short: "h" },
|
|
444218
|
-
to: { type: "string" }
|
|
444328
|
+
to: { type: "string" },
|
|
444329
|
+
conversation: { type: "string" },
|
|
444330
|
+
agent: { type: "string" }
|
|
444219
444331
|
};
|
|
444220
444332
|
});
|
|
444221
444333
|
|
|
@@ -450467,6 +450579,36 @@ var init_channel_rich_draft_streamer = __esm(() => {
|
|
|
450467
450579
|
]);
|
|
450468
450580
|
});
|
|
450469
450581
|
|
|
450582
|
+
// src/channels/gateway-sources.ts
|
|
450583
|
+
function sourceRouteKey(source) {
|
|
450584
|
+
return [
|
|
450585
|
+
source.channel,
|
|
450586
|
+
source.accountId ?? "",
|
|
450587
|
+
source.chatId,
|
|
450588
|
+
source.threadId ?? ""
|
|
450589
|
+
].join(":");
|
|
450590
|
+
}
|
|
450591
|
+
function sourceLifecycleKey(source) {
|
|
450592
|
+
return [
|
|
450593
|
+
sourceRouteKey(source),
|
|
450594
|
+
source.messageId ?? "",
|
|
450595
|
+
source.agentId,
|
|
450596
|
+
source.conversationId
|
|
450597
|
+
].join(":");
|
|
450598
|
+
}
|
|
450599
|
+
function uniqueSourcesBy(sources, getKey) {
|
|
450600
|
+
const byKey = new Map;
|
|
450601
|
+
for (const source of sources)
|
|
450602
|
+
byKey.set(getKey(source), source);
|
|
450603
|
+
return [...byKey.values()];
|
|
450604
|
+
}
|
|
450605
|
+
function uniqueRoutedSources(sources) {
|
|
450606
|
+
return uniqueSourcesBy(sources, sourceRouteKey);
|
|
450607
|
+
}
|
|
450608
|
+
function uniqueLifecycleSources(sources) {
|
|
450609
|
+
return uniqueSourcesBy(sources, sourceLifecycleKey);
|
|
450610
|
+
}
|
|
450611
|
+
|
|
450470
450612
|
// src/channels/progress-builder.ts
|
|
450471
450613
|
function getMessageType(delta2) {
|
|
450472
450614
|
return firstNonEmptyString2(delta2.message_type, delta2.messageType) ?? null;
|
|
@@ -450519,6 +450661,34 @@ function toolNameForMessage(summary) {
|
|
|
450519
450661
|
function createChannelTurnProgressBuilder(options = {}) {
|
|
450520
450662
|
const argumentsByToolCallId = new Map;
|
|
450521
450663
|
const namesByToolCallId = new Map;
|
|
450664
|
+
const batchesByStep = new Map;
|
|
450665
|
+
const batchesByToolCallId = new Map;
|
|
450666
|
+
function addToolBatchTitles(delta2, updates) {
|
|
450667
|
+
const record5 = asRecord4(delta2);
|
|
450668
|
+
if (!record5)
|
|
450669
|
+
return updates;
|
|
450670
|
+
const messageType = getMessageType(record5);
|
|
450671
|
+
const isToolRequest = messageType === "tool_call_message" || messageType === "approval_request_message";
|
|
450672
|
+
const stepKey = isToolRequest ? firstNonEmptyString2(record5.step_id, record5.id) : undefined;
|
|
450673
|
+
let requestBatch = stepKey ? batchesByStep.get(stepKey) : undefined;
|
|
450674
|
+
if (stepKey && !requestBatch) {
|
|
450675
|
+
requestBatch = { title: null };
|
|
450676
|
+
batchesByStep.set(stepKey, requestBatch);
|
|
450677
|
+
}
|
|
450678
|
+
return updates.map((update3) => {
|
|
450679
|
+
if (update3.kind !== "tool")
|
|
450680
|
+
return update3;
|
|
450681
|
+
const batch = (update3.toolCallId ? batchesByToolCallId.get(update3.toolCallId) : undefined) ?? requestBatch;
|
|
450682
|
+
if (!batch)
|
|
450683
|
+
return update3;
|
|
450684
|
+
if (update3.toolCallId)
|
|
450685
|
+
batchesByToolCallId.set(update3.toolCallId, batch);
|
|
450686
|
+
if (batch.title === null && update3.state === "started") {
|
|
450687
|
+
batch.title = firstNonEmptyString2(update3.toolTitle, update3.toolDetails) ?? null;
|
|
450688
|
+
}
|
|
450689
|
+
return { ...update3, toolBatchTitle: batch.title };
|
|
450690
|
+
});
|
|
450691
|
+
}
|
|
450522
450692
|
function extractToolCallSummary(value) {
|
|
450523
450693
|
const record5 = asRecord4(value);
|
|
450524
450694
|
if (!record5) {
|
|
@@ -450835,7 +451005,9 @@ function createChannelTurnProgressBuilder(options = {}) {
|
|
|
450835
451005
|
return [];
|
|
450836
451006
|
}
|
|
450837
451007
|
}
|
|
450838
|
-
return {
|
|
451008
|
+
return {
|
|
451009
|
+
buildUpdates: (delta2) => addToolBatchTitles(delta2, buildUpdates(delta2))
|
|
451010
|
+
};
|
|
450839
451011
|
}
|
|
450840
451012
|
var init_progress_builder = __esm(() => {
|
|
450841
451013
|
init_progress_formatting();
|
|
@@ -450848,34 +451020,6 @@ function runtimeKey(runtime) {
|
|
|
450848
451020
|
function hasAgentRuntime(value) {
|
|
450849
451021
|
return !!value.runtime?.agent_id;
|
|
450850
451022
|
}
|
|
450851
|
-
function sourceRouteKey(source) {
|
|
450852
|
-
return [
|
|
450853
|
-
source.channel,
|
|
450854
|
-
source.accountId ?? "",
|
|
450855
|
-
source.chatId,
|
|
450856
|
-
source.threadId ?? ""
|
|
450857
|
-
].join(":");
|
|
450858
|
-
}
|
|
450859
|
-
function sourceLifecycleKey(source) {
|
|
450860
|
-
return [
|
|
450861
|
-
sourceRouteKey(source),
|
|
450862
|
-
source.messageId ?? "",
|
|
450863
|
-
source.agentId,
|
|
450864
|
-
source.conversationId
|
|
450865
|
-
].join(":");
|
|
450866
|
-
}
|
|
450867
|
-
function uniqueSourcesBy(sources, getKey) {
|
|
450868
|
-
const byKey = new Map;
|
|
450869
|
-
for (const source of sources)
|
|
450870
|
-
byKey.set(getKey(source), source);
|
|
450871
|
-
return [...byKey.values()];
|
|
450872
|
-
}
|
|
450873
|
-
function uniqueRoutedSources(sources) {
|
|
450874
|
-
return uniqueSourcesBy(sources, sourceRouteKey);
|
|
450875
|
-
}
|
|
450876
|
-
function uniqueLifecycleSources(sources) {
|
|
450877
|
-
return uniqueSourcesBy(sources, sourceLifecycleKey);
|
|
450878
|
-
}
|
|
450879
451023
|
function channelTagsForSources(sources) {
|
|
450880
451024
|
return [...new Set(sources.map((source) => `channel:${source.channel}`))];
|
|
450881
451025
|
}
|
|
@@ -450934,6 +451078,7 @@ class ChannelGateway {
|
|
|
450934
451078
|
state.acceptedClientMessageIds.add(delivery.clientMessageId);
|
|
450935
451079
|
return true;
|
|
450936
451080
|
}
|
|
451081
|
+
const workAtSubmit = state.active || state.pendingSourcesByClientMessageId.size > 0;
|
|
450937
451082
|
state.pendingSourcesByClientMessageId.set(delivery.clientMessageId, {
|
|
450938
451083
|
sources: uniqueLifecycleSources(delivery.sources),
|
|
450939
451084
|
disposition: "submitting"
|
|
@@ -450962,6 +451107,8 @@ class ChannelGateway {
|
|
|
450962
451107
|
});
|
|
450963
451108
|
if (!response.accepted) {
|
|
450964
451109
|
state.pendingSourcesByClientMessageId.delete(delivery.clientMessageId);
|
|
451110
|
+
if (workAtSubmit && !state.active)
|
|
451111
|
+
this.finishRejectedDelivery(state, delivery);
|
|
450965
451112
|
return false;
|
|
450966
451113
|
}
|
|
450967
451114
|
this.rememberAcceptedClientMessageId(state, delivery.clientMessageId);
|
|
@@ -450981,9 +451128,28 @@ class ChannelGateway {
|
|
|
450981
451128
|
return true;
|
|
450982
451129
|
} catch (error5) {
|
|
450983
451130
|
state.pendingSourcesByClientMessageId.delete(delivery.clientMessageId);
|
|
451131
|
+
if (workAtSubmit && !state.active)
|
|
451132
|
+
this.finishRejectedDelivery(state, delivery);
|
|
450984
451133
|
throw error5;
|
|
450985
451134
|
}
|
|
450986
451135
|
}
|
|
451136
|
+
remainingSources(state) {
|
|
451137
|
+
return uniqueLifecycleSources([
|
|
451138
|
+
...state.active?.lifecycleSources ?? [],
|
|
451139
|
+
...Array.from(state.pendingSourcesByClientMessageId.values()).flatMap((pending) => pending.sources)
|
|
451140
|
+
]);
|
|
451141
|
+
}
|
|
451142
|
+
finishRejectedDelivery(state, delivery) {
|
|
451143
|
+
const remainingSources = this.remainingSources(state);
|
|
451144
|
+
this.enqueueHook(state, () => this.hooks.onLifecycle({
|
|
451145
|
+
type: "finished",
|
|
451146
|
+
batchId: `channel-${delivery.clientMessageId}`,
|
|
451147
|
+
sources: delivery.sources,
|
|
451148
|
+
stopReason: "cancelled",
|
|
451149
|
+
outcome: "cancelled",
|
|
451150
|
+
remainingSources
|
|
451151
|
+
}));
|
|
451152
|
+
}
|
|
450987
451153
|
adoptQueuedDelivery(delivery) {
|
|
450988
451154
|
const state = this.getState(delivery.runtime);
|
|
450989
451155
|
const sources = uniqueLifecycleSources(delivery.sources);
|
|
@@ -451366,12 +451532,14 @@ class ChannelGateway {
|
|
|
451366
451532
|
this.activateSources(state, firstDequeued.clientMessageId, dequeued.flatMap((entry) => entry.sources));
|
|
451367
451533
|
}
|
|
451368
451534
|
for (const entry of cancelled) {
|
|
451535
|
+
const remainingSources = this.remainingSources(state);
|
|
451369
451536
|
this.enqueueHook(state, () => this.hooks.onLifecycle({
|
|
451370
451537
|
type: "finished",
|
|
451371
451538
|
batchId: `channel-${entry.clientMessageId}`,
|
|
451372
451539
|
sources: entry.sources,
|
|
451373
451540
|
outcome: "cancelled",
|
|
451374
|
-
stopReason: "cancelled"
|
|
451541
|
+
stopReason: "cancelled",
|
|
451542
|
+
...remainingSources.length ? { remainingSources } : {}
|
|
451375
451543
|
}));
|
|
451376
451544
|
}
|
|
451377
451545
|
}
|
|
@@ -451448,12 +451616,14 @@ class ChannelGateway {
|
|
|
451448
451616
|
return;
|
|
451449
451617
|
state.active = null;
|
|
451450
451618
|
active.richDraft?.dispose();
|
|
451619
|
+
const remainingSources = lifecycleOutcome(terminal.stopReason) === "completed" ? this.remainingSources(state) : [];
|
|
451451
451620
|
this.enqueueHook(state, () => this.hooks.onLifecycle({
|
|
451452
451621
|
type: "finished",
|
|
451453
451622
|
batchId: active.batchId,
|
|
451454
451623
|
sources: active.lifecycleSources,
|
|
451455
451624
|
outcome: lifecycleOutcome(terminal.stopReason),
|
|
451456
451625
|
stopReason: terminal.stopReason,
|
|
451626
|
+
...remainingSources.length ? { remainingSources } : {},
|
|
451457
451627
|
...terminal.runId ?? active.runId ? { runId: terminal.runId ?? active.runId } : {},
|
|
451458
451628
|
...terminal.error ? { error: terminal.error } : {}
|
|
451459
451629
|
}));
|
|
@@ -520812,4 +520982,4 @@ function registerBunOAuthFlows() {
|
|
|
520812
520982
|
registerBunOAuthFlows();
|
|
520813
520983
|
await init_src5().then(() => exports_src2);
|
|
520814
520984
|
|
|
520815
|
-
//# debugId=
|
|
520985
|
+
//# debugId=95B2BCB7592A39B564756E2164756E21
|
package/package.json
CHANGED
|
@@ -37,7 +37,7 @@ else:
|
|
|
37
37
|
with open("robot-mascot.png", "wb") as f:
|
|
38
38
|
f.write(data)
|
|
39
39
|
|
|
40
|
-
print("saved robot-mascot.png
|
|
40
|
+
print("saved robot-mascot.png")
|
|
41
41
|
PY
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -63,7 +63,9 @@ The Letta Code UI renders local file paths in markdown image tags, so the image
|
|
|
63
63
|
appears inline. **Always display generated images this way** — don't just report
|
|
64
64
|
the path, and never paste the raw base64 / a `data:` URI. The markdown path must
|
|
65
65
|
match where you saved the file. For `n > 1`, save each image to its own file and
|
|
66
|
-
embed each on its own line.
|
|
66
|
+
embed each on its own line. Keep credit amounts and billing metadata out of
|
|
67
|
+
user-facing replies and captions unless the user asks about cost. When asked,
|
|
68
|
+
read `billing.credits_charged` from the saved response.
|
|
67
69
|
|
|
68
70
|
## Request body
|
|
69
71
|
|
|
@@ -117,8 +119,7 @@ DATA_URL="data:image/png;base64,$(base64 < input.png | tr -d '\n')"
|
|
|
117
119
|
|
|
118
120
|
## Notes
|
|
119
121
|
|
|
120
|
-
- **Billing**: every success charges credits; don't loop needlessly
|
|
121
|
-
`credits_charged`.
|
|
122
|
+
- **Billing**: every success charges credits; don't loop needlessly.
|
|
122
123
|
- **Errors**: `402` = insufficient credits (`credits_required` in body); `400`/`500`
|
|
123
124
|
return `{ "message": "..." }` — surface it to the user.
|
|
124
125
|
- Only `flux`, `gemini`, and `openai` are supported here.
|