@letta-ai/letta-code 0.34.27 → 0.34.28
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-slack.js +2 -2
- package/dist/channels-slack.js.map +3 -3
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/mcp-oauth.js +2 -2
- package/dist/mcp-oauth.js.map +1 -1
- package/dist/types/channels/slack/attachment-primitives.d.ts.map +1 -1
- package/dist/types/tools/impl/monitor-script.d.ts +38 -0
- package/dist/types/tools/impl/monitor-script.d.ts.map +1 -0
- package/dist/types/tools/impl/monitor.d.ts +2 -0
- package/dist/types/tools/impl/monitor.d.ts.map +1 -1
- package/dist/types/tools/impl/process_manager.d.ts +1 -1
- package/dist/types/tools/impl/process_manager.d.ts.map +1 -1
- package/dist/types/tools/manager.d.ts.map +1 -1
- package/dist/types/tools/script-tool-scope.d.ts.map +1 -1
- package/dist/types/tools/tool-context-retention.d.ts +7 -0
- package/dist/types/tools/tool-context-retention.d.ts.map +1 -0
- package/dist/types/tools/workflow/workflow-engine.d.ts +4 -1
- package/dist/types/tools/workflow/workflow-engine.d.ts.map +1 -1
- package/dist/types/types/background-process-protocol.d.ts +1 -1
- package/dist/types/types/background-process-protocol.d.ts.map +1 -1
- package/letta.js +406 -53
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/scripts/run-api-integration-tests.ts +36 -0
- package/scripts/source-file-size-baseline.json +1 -1
package/letta.js
CHANGED
|
@@ -6044,7 +6044,7 @@ var package_default;
|
|
|
6044
6044
|
var init_package = __esm(() => {
|
|
6045
6045
|
package_default = {
|
|
6046
6046
|
name: "@letta-ai/letta-code",
|
|
6047
|
-
version: "0.34.
|
|
6047
|
+
version: "0.34.28",
|
|
6048
6048
|
lettaStartupLogProtocol: 1,
|
|
6049
6049
|
lettaConfigDirProtocol: 1,
|
|
6050
6050
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
@@ -168979,7 +168979,7 @@ async function resolveSlackMessageFiles(params) {
|
|
|
168979
168979
|
oldest: params.messageTs,
|
|
168980
168980
|
latest: params.messageTs,
|
|
168981
168981
|
inclusive: true,
|
|
168982
|
-
limit:
|
|
168982
|
+
limit: 2
|
|
168983
168983
|
}));
|
|
168984
168984
|
const message2 = messages2.find((entry) => entry.ts === params.messageTs);
|
|
168985
168985
|
return message2 ? collectSlackFiles(message2) : null;
|
|
@@ -211476,7 +211476,7 @@ var init_mcp_client = __esm(() => {
|
|
|
211476
211476
|
init_streamableHttp();
|
|
211477
211477
|
DEFAULT_CLIENT_INFO = {
|
|
211478
211478
|
name: "letta-code",
|
|
211479
|
-
version: "0.34.
|
|
211479
|
+
version: "0.34.28"
|
|
211480
211480
|
};
|
|
211481
211481
|
});
|
|
211482
211482
|
|
|
@@ -215530,7 +215530,7 @@ Important:
|
|
|
215530
215530
|
var init_Bash = () => {};
|
|
215531
215531
|
|
|
215532
215532
|
// src/tools/descriptions/CodeMode.md
|
|
215533
|
-
var CodeMode_default = "Run a JavaScript async function body that calls tools and returns only the results you need.\n\nCall built-in tools with `await tools.<name>(args)` and MCP tools with `await tools.mcp__server__tool(args)`; `listTools()` and `describeTool(name)` list tools and their schemas. Failed calls throw. `args` holds optional input, and `console.log()` output is captured.\n\nReturns `{ result, output }`. Like Bash, a script still running after a brief wait continues in the background and its result arrives as a task notification; `run_in_background: true` returns the task ID immediately. TaskStop aborts it.\n\nNested calls follow permission rules; calls that need approval fail, so call those tools directly. This is not a security sandbox.\n\nExample:\n\n const texts = await Promise.all(args.paths.map(file_path => tools.Read({ file_path })));\n return texts.map((text, i) => ({ path: args.paths[i], lines: text.split('\\n').length }));\n";
|
|
215533
|
+
var CodeMode_default = "Run a JavaScript async function body that calls tools and returns only the results you need.\n\nCall built-in and server-side tools with `await tools.<name>(args)` and MCP tools with `await tools.mcp__server__tool(args)`; `listTools()` and `describeTool(name)` list tools and their schemas. Failed calls throw. `args` holds optional input, and `console.log()` output is captured.\n\nReturns `{ result, output }`. Like Bash, a script still running after a brief wait continues in the background and its result arrives as a task notification; `run_in_background: true` returns the task ID immediately. TaskStop aborts it.\n\nNested calls follow permission rules; calls that need approval fail, so call those tools directly. This is not a security sandbox.\n\nExample:\n\n const texts = await Promise.all(args.paths.map(file_path => tools.Read({ file_path })));\n return texts.map((text, i) => ({ path: args.paths[i], lines: text.split('\\n').length }));\n";
|
|
215534
215534
|
var init_CodeMode = () => {};
|
|
215535
215535
|
|
|
215536
215536
|
// src/tools/descriptions/Edit.md
|
|
@@ -215634,7 +215634,7 @@ Do not use this tool for root memory files (already loaded) or skills/ (use Skil
|
|
|
215634
215634
|
var init_Memory = () => {};
|
|
215635
215635
|
|
|
215636
215636
|
// src/tools/descriptions/Monitor.md
|
|
215637
|
-
var Monitor_default = 'Start a background monitor that streams events from a long-running script. Each stdout line is an event — you keep working and notifications arrive in the chat. Events arrive on their own schedule and are not replies from the user, even if one lands while you\'re waiting for the user to answer a question.\n\nPick by how many notifications you need:\n- **One** ("tell me when the server is ready / the build finishes") → use **Bash with `run_in_background`** and a command that exits when the condition is true, e.g. `until grep -q "Ready in" dev.log; do sleep 0.5; done`. You get a single completion notification when it exits.\n- **One per occurrence, indefinitely** ("tell me every time an ERROR line appears") → Monitor with an unbounded command (`tail -f`, `inotifywait -m`, `while true`).\n- **One per occurrence, until a known end** ("emit each CI step result, stop when the run completes") → Monitor with a command that emits lines and then exits.\n\nYour script\'s stdout is the event stream. Each line becomes a notification. Exit ends the watch.\n # Each matching log line is an event\n tail -f /var/log/app.log | grep --line-buffered "ERROR"\n # Each file change is an event\n inotifywait -m --format \'%e %f\' /watched/dir\n # Node script that emits events as they arrive (e.g. WebSocket listener)\n node watch-for-events.js\n\nFor open github.com pull requests, use WatchPR instead. It covers the whole PR surface and keeps watching after CI finishes. Use Monitor only when WatchPR does not apply.\n\n**Don\'t use an unbounded command for a single notification.** `tail -f`, `inotifywait -m`, and `while true` never exit on their own, so the monitor stays armed until timeout even after the event has fired. For "tell me when X is ready," use Bash `run_in_background` with an `until` loop instead (one notification, ends in seconds). Note that `tail -f log | grep -m 1 ...` does *not* fix this: if the log goes quiet after the match, `tail` never receives SIGPIPE and the pipeline hangs anyway.\n\n- Every pipe stage must flush per line or matches sit in its buffer unseen: `grep` needs `--line-buffered`, `awk` needs `fflush()`. `head` cannot flush at all — `| head -N` delivers nothing until N matches accumulate, then ends the stream.\n- In poll loops, handle transient failures (`curl ... || true`) — one failed request shouldn\'t kill the monitor.\n- Poll intervals: 30s+ for remote APIs (rate limits), 0.5–1s for local checks.\n- Write a specific `description` — it appears in every notification ("errors in deploy.log" not "watching logs").\n- Only stdout is the event stream. Stderr goes to the output file (readable via Read) but does not trigger notifications — for a command you run directly (e.g. `python train.py 2>&1 | grep --line-buffered ...`), merge stderr with `2>&1` so its failures reach your filter. (No effect on `tail -f` of an existing log — that file only contains what its writer redirected.)\n\n**Coverage — silence is not success.** When watching a job or process for an outcome, your filter must match every terminal state, not just the happy path. A monitor that greps only for the success marker stays silent through a crashloop, a hung process, or an unexpected exit — and silence looks identical to "still running." Before arming, ask: *if this process crashed right now, would my filter emit anything?* If not, widen it.\n # Wrong — silent on crash, hang, or any non-success exit\n tail -f run.log | grep --line-buffered "elapsed_steps="\n # Right — one alternation covering progress + the failure signatures you\'d act on\n tail -f run.log | grep -E --line-buffered "elapsed_steps=|Traceback|Error|FAILED|assert|Killed|OOM"\n\nFor poll loops checking job state, emit on every terminal status (`succeeded|failed|cancelled|timeout`), not just success. If you cannot confidently enumerate the failure signatures, broaden the grep alternation rather than narrow it — some extra noise is better than missing a crashloop.\n\n**Output volume**: Every stdout line is a conversation message, so the filter should be selective — but selective means "the lines you\'d act on," not "only good news." Never pipe raw logs; filter to exactly the success and failure signals you care about. Monitors that produce too many events are automatically stopped; restart with a tighter filter if this happens.\n\nStdout lines within 200ms are batched into a single notification, so multiline output from a single event groups naturally.\n\nThe script runs in the same shell environment as Bash. Exit ends the watch (exit code is reported). Timeout → killed. Set `persistent: true` for session-length watches (log tails, long-lived event streams). In managed Cloud sandboxes, every monitor stops after at most 1 hour, including persistent monitors; elsewhere persistent monitors run until TaskStop or session end. For longer Cloud waits, schedule a Wake or cron (which does not keep the sandbox running between turns) and combine it with shorter monitors. Use TaskStop to cancel early.\n\nWhen someone is waiting on the result, set `backup_after_seconds` (e.g. 900). If the monitor goes that long without an event, a durable Wake brings you back to check the real state, so a queued, hung, or dead watch can\'t leave them waiting in silence.\n\n**ws source** — open a WebSocket and stream each incoming text frame as an event. No shell, no polling: the server pushes, you get notified.\n ws: {url: \'wss://events.example.com/stream\', protocols: [\'v1\']},\n description: \'deploy events\',\n\nEach text frame becomes one notification (multiline frames stay as one event). Binary frames are reported as `[binary frame, N bytes]` rather than passed through. Socket close ends the watch with the close code surfaced; errors are surfaced before close. Same rate limiting as bash — a firehose will be suppressed and eventually stopped, so subscribe to a filtered feed where one exists.\n\nPrefer this over `command: \'websocat wss://…\'` — it avoids the extra process and line-buffering pitfalls. Use bash when you need to transform or filter frames with shell tools before they become events.\n';
|
|
215637
|
+
var Monitor_default = 'Start a background monitor that streams events from a long-running script. Each stdout line is an event — you keep working and notifications arrive in the chat. Events arrive on their own schedule and are not replies from the user, even if one lands while you\'re waiting for the user to answer a question.\n\nPick by how many notifications you need:\n- **One** ("tell me when the server is ready / the build finishes") → use **Bash with `run_in_background`** and a command that exits when the condition is true, e.g. `until grep -q "Ready in" dev.log; do sleep 0.5; done`. You get a single completion notification when it exits.\n- **One per occurrence, indefinitely** ("tell me every time an ERROR line appears") → Monitor with an unbounded command (`tail -f`, `inotifywait -m`, `while true`).\n- **One per occurrence, until a known end** ("emit each CI step result, stop when the run completes") → Monitor with a command that emits lines and then exits.\n\nYour script\'s stdout is the event stream. Each line becomes a notification. Exit ends the watch.\n # Each matching log line is an event\n tail -f /var/log/app.log | grep --line-buffered "ERROR"\n # Each file change is an event\n inotifywait -m --format \'%e %f\' /watched/dir\n # Node script that emits events as they arrive (e.g. WebSocket listener)\n node watch-for-events.js\n\nFor open github.com pull requests, use WatchPR instead. It covers the whole PR surface and keeps watching after CI finishes. Use Monitor only when WatchPR does not apply.\n\n**Don\'t use an unbounded command for a single notification.** `tail -f`, `inotifywait -m`, and `while true` never exit on their own, so the monitor stays armed until timeout even after the event has fired. For "tell me when X is ready," use Bash `run_in_background` with an `until` loop instead (one notification, ends in seconds). Note that `tail -f log | grep -m 1 ...` does *not* fix this: if the log goes quiet after the match, `tail` never receives SIGPIPE and the pipeline hangs anyway.\n\n- Every pipe stage must flush per line or matches sit in its buffer unseen: `grep` needs `--line-buffered`, `awk` needs `fflush()`. `head` cannot flush at all — `| head -N` delivers nothing until N matches accumulate, then ends the stream.\n- In poll loops, handle transient failures (`curl ... || true`) — one failed request shouldn\'t kill the monitor.\n- Poll intervals: 30s+ for remote APIs (rate limits), 0.5–1s for local checks.\n- Write a specific `description` — it appears in every notification ("errors in deploy.log" not "watching logs").\n- Only stdout is the event stream. Stderr goes to the output file (readable via Read) but does not trigger notifications — for a command you run directly (e.g. `python train.py 2>&1 | grep --line-buffered ...`), merge stderr with `2>&1` so its failures reach your filter. (No effect on `tail -f` of an existing log — that file only contains what its writer redirected.)\n\n**Coverage — silence is not success.** When watching a job or process for an outcome, your filter must match every terminal state, not just the happy path. A monitor that greps only for the success marker stays silent through a crashloop, a hung process, or an unexpected exit — and silence looks identical to "still running." Before arming, ask: *if this process crashed right now, would my filter emit anything?* If not, widen it.\n # Wrong — silent on crash, hang, or any non-success exit\n tail -f run.log | grep --line-buffered "elapsed_steps="\n # Right — one alternation covering progress + the failure signatures you\'d act on\n tail -f run.log | grep -E --line-buffered "elapsed_steps=|Traceback|Error|FAILED|assert|Killed|OOM"\n\nFor poll loops checking job state, emit on every terminal status (`succeeded|failed|cancelled|timeout`), not just success. If you cannot confidently enumerate the failure signatures, broaden the grep alternation rather than narrow it — some extra noise is better than missing a crashloop.\n\n**Output volume**: Every stdout line is a conversation message, so the filter should be selective — but selective means "the lines you\'d act on," not "only good news." Never pipe raw logs; filter to exactly the success and failure signals you care about. Monitors that produce too many events are automatically stopped; restart with a tighter filter if this happens.\n\nStdout lines within 200ms are batched into a single notification, so multiline output from a single event groups naturally.\n\nThe script runs in the same shell environment as Bash. Exit ends the watch (exit code is reported). Timeout → killed. Set `persistent: true` for session-length watches (log tails, long-lived event streams). In managed Cloud sandboxes, every monitor stops after at most 1 hour, including persistent monitors; elsewhere persistent monitors run until TaskStop or session end. For longer Cloud waits, schedule a Wake or cron (which does not keep the sandbox running between turns) and combine it with shorter monitors. Use TaskStop to cancel early.\n\nWhen someone is waiting on the result, set `backup_after_seconds` (e.g. 900). If the monitor goes that long without an event, a durable Wake brings you back to check the real state, so a queued, hung, or dead watch can\'t leave them waiting in silence.\n\n**ws source** — open a WebSocket and stream each incoming text frame as an event. No shell, no polling: the server pushes, you get notified.\n ws: {url: \'wss://events.example.com/stream\', protocols: [\'v1\']},\n description: \'deploy events\',\n\nEach text frame becomes one notification (multiline frames stay as one event). Binary frames are reported as `[binary frame, N bytes]` rather than passed through. Socket close ends the watch with the close code surfaced; errors are surfaced before close. Same rate limiting as bash — a firehose will be suppressed and eventually stopped, so subscribe to a filtered feed where one exists.\n\nPrefer this over `command: \'websocat wss://…\'` — it avoids the extra process and line-buffering pitfalls. Use bash when you need to transform or filter frames with shell tools before they become events.\n\n**script source** — a JavaScript watch loop with the same globals as CodeMode, plus `emit(text)` (one event) and `sleep(ms)`. Returning or throwing ends the watch.\n';
|
|
215638
215638
|
var init_Monitor = () => {};
|
|
215639
215639
|
|
|
215640
215640
|
// src/tools/descriptions/Read.md
|
|
@@ -224201,6 +224201,34 @@ var init_external_tool_background_eligibility = __esm(() => {
|
|
|
224201
224201
|
listenerRegistrations = new WeakMap;
|
|
224202
224202
|
});
|
|
224203
224203
|
|
|
224204
|
+
// src/tools/tool-context-retention.ts
|
|
224205
|
+
function retainToolContext(contextId) {
|
|
224206
|
+
holders.set(contextId, (holders.get(contextId) ?? 0) + 1);
|
|
224207
|
+
}
|
|
224208
|
+
function releaseRetainedToolContext(contextId) {
|
|
224209
|
+
const count = (holders.get(contextId) ?? 0) - 1;
|
|
224210
|
+
if (count > 0) {
|
|
224211
|
+
holders.set(contextId, count);
|
|
224212
|
+
return false;
|
|
224213
|
+
}
|
|
224214
|
+
holders.delete(contextId);
|
|
224215
|
+
return deferredReleases.delete(contextId);
|
|
224216
|
+
}
|
|
224217
|
+
function isToolContextRetained(contextId) {
|
|
224218
|
+
return holders.has(contextId);
|
|
224219
|
+
}
|
|
224220
|
+
function deferToolContextRelease(contextId) {
|
|
224221
|
+
if (!holders.has(contextId))
|
|
224222
|
+
return false;
|
|
224223
|
+
deferredReleases.add(contextId);
|
|
224224
|
+
return true;
|
|
224225
|
+
}
|
|
224226
|
+
var holders, deferredReleases;
|
|
224227
|
+
var init_tool_context_retention = __esm(() => {
|
|
224228
|
+
holders = new Map;
|
|
224229
|
+
deferredReleases = new Set;
|
|
224230
|
+
});
|
|
224231
|
+
|
|
224204
224232
|
// src/tools/external-tool-content.ts
|
|
224205
224233
|
function normalizeExternalToolResultContent(content) {
|
|
224206
224234
|
const normalized = content.flatMap((part) => {
|
|
@@ -231005,12 +231033,11 @@ function saveExecutionContext(snapshot) {
|
|
|
231005
231033
|
const contexts = getExecutionContexts();
|
|
231006
231034
|
const contextId = `ctx-${Date.now()}-${toolExecutionContextCounter++}`;
|
|
231007
231035
|
contexts.set(contextId, snapshot);
|
|
231008
|
-
const
|
|
231009
|
-
|
|
231010
|
-
|
|
231011
|
-
if (
|
|
231012
|
-
contexts.delete(
|
|
231013
|
-
}
|
|
231036
|
+
for (const id2 of contexts.keys()) {
|
|
231037
|
+
if (contexts.size <= 4096)
|
|
231038
|
+
break;
|
|
231039
|
+
if (!isToolContextRetained(id2))
|
|
231040
|
+
contexts.delete(id2);
|
|
231014
231041
|
}
|
|
231015
231042
|
return contextId;
|
|
231016
231043
|
}
|
|
@@ -231056,7 +231083,8 @@ function clearCapturedToolExecutionContexts() {
|
|
|
231056
231083
|
getExecutionContexts().clear();
|
|
231057
231084
|
}
|
|
231058
231085
|
function releaseToolExecutionContext(contextId) {
|
|
231059
|
-
|
|
231086
|
+
if (!deferToolContextRelease(contextId))
|
|
231087
|
+
getExecutionContexts().delete(contextId);
|
|
231060
231088
|
}
|
|
231061
231089
|
function acquireSwitchLock() {
|
|
231062
231090
|
const lock = getSwitchLock();
|
|
@@ -232208,6 +232236,7 @@ var init_manager2 = __esm(async () => {
|
|
|
232208
232236
|
init_telemetry();
|
|
232209
232237
|
init_external_tool_background();
|
|
232210
232238
|
init_external_tool_background_eligibility();
|
|
232239
|
+
init_tool_context_retention();
|
|
232211
232240
|
init_checkout_readiness();
|
|
232212
232241
|
init_debug();
|
|
232213
232242
|
init_secrets_store();
|
|
@@ -234219,6 +234248,51 @@ var init_mcp = __esm(() => {
|
|
|
234219
234248
|
|
|
234220
234249
|
// src/tools/script-tool-scope.ts
|
|
234221
234250
|
import { randomUUID as randomUUID33 } from "node:crypto";
|
|
234251
|
+
async function loadServerTools(agentId, builtins) {
|
|
234252
|
+
const tools = new Map;
|
|
234253
|
+
if (!agentId || !getBackend().capabilities.serverSideToolManagement) {
|
|
234254
|
+
return tools;
|
|
234255
|
+
}
|
|
234256
|
+
try {
|
|
234257
|
+
const agent = await (await getClient()).agents.retrieve(agentId, {
|
|
234258
|
+
include: ["agent.tools"]
|
|
234259
|
+
});
|
|
234260
|
+
const approvalRules = new Set((agent.tool_rules ?? []).filter((rule) => rule.type === "requires_approval").map((rule) => rule.tool_name));
|
|
234261
|
+
for (const tool of agent.tools ?? []) {
|
|
234262
|
+
const name = tool.name;
|
|
234263
|
+
if (!name || tool.tool_type === "external_mcp")
|
|
234264
|
+
continue;
|
|
234265
|
+
if (builtins.has(name) || SCRIPT_EXCLUDED_TOOLS.has(name))
|
|
234266
|
+
continue;
|
|
234267
|
+
tools.set(name, {
|
|
234268
|
+
name,
|
|
234269
|
+
description: tool.description ?? "",
|
|
234270
|
+
inputSchema: tool.json_schema?.parameters ?? {},
|
|
234271
|
+
requiresApproval: tool.default_requires_approval === true || approvalRules.has(name)
|
|
234272
|
+
});
|
|
234273
|
+
}
|
|
234274
|
+
} catch {}
|
|
234275
|
+
return tools;
|
|
234276
|
+
}
|
|
234277
|
+
async function callServerTool(agentId, tool, args, signal) {
|
|
234278
|
+
if (!agentId)
|
|
234279
|
+
throw new Error(`${tool.name} requires an invoking agent.`);
|
|
234280
|
+
if (tool.requiresApproval) {
|
|
234281
|
+
throw new Error(`${tool.name} needs approval, which a script cannot request; call it directly.`);
|
|
234282
|
+
}
|
|
234283
|
+
const result2 = await (await getClient()).agents.tools.run(tool.name, { agent_id: agentId, args }, signal ? { signal } : {});
|
|
234284
|
+
const value = result2.status === "success" ? result2.func_return : result2.stderr?.join(`
|
|
234285
|
+
`) ?? result2.func_return;
|
|
234286
|
+
return {
|
|
234287
|
+
content: [
|
|
234288
|
+
{
|
|
234289
|
+
type: "text",
|
|
234290
|
+
text: typeof value === "string" ? value : JSON.stringify(value ?? null)
|
|
234291
|
+
}
|
|
234292
|
+
],
|
|
234293
|
+
isError: result2.status !== "success"
|
|
234294
|
+
};
|
|
234295
|
+
}
|
|
234222
234296
|
function isOwningAgentId(value) {
|
|
234223
234297
|
return value?.startsWith("agent-") === true && !value.startsWith("agent-free:");
|
|
234224
234298
|
}
|
|
@@ -234245,8 +234319,9 @@ async function createScriptToolScope(parentAgentId, signal) {
|
|
|
234245
234319
|
const permissionCwd = getCurrentWorkingDirectory();
|
|
234246
234320
|
const context3 = toolContextId ? manager.getExecutionContextById(toolContextId) : undefined;
|
|
234247
234321
|
const builtinSchemas = (context3 ? serializeClientTools(context3.toolRegistry, context3.externalTools, context3.modTools, manager.getServerToolName) : []).filter((schema5) => !SCRIPT_EXCLUDED_TOOLS.has(schema5.name));
|
|
234248
|
-
const
|
|
234249
|
-
const
|
|
234322
|
+
const builtins = new Set(builtinSchemas.map((schema5) => schema5.name));
|
|
234323
|
+
const serverTools = await loadServerTools(parentAgentId, builtins);
|
|
234324
|
+
const builtinToolNames = [...builtins, ...serverTools.keys()];
|
|
234250
234325
|
let mcpSession;
|
|
234251
234326
|
const mcp = () => {
|
|
234252
234327
|
if (!parentAgentId)
|
|
@@ -234264,6 +234339,7 @@ async function createScriptToolScope(parentAgentId, signal) {
|
|
|
234264
234339
|
description: schema5.description,
|
|
234265
234340
|
inputSchema: schema5.parameters
|
|
234266
234341
|
})),
|
|
234342
|
+
...serverTools.values(),
|
|
234267
234343
|
...mcpTools.map((tool) => ({
|
|
234268
234344
|
name: tool.name,
|
|
234269
234345
|
description: tool.description ?? "",
|
|
@@ -234273,6 +234349,10 @@ async function createScriptToolScope(parentAgentId, signal) {
|
|
|
234273
234349
|
},
|
|
234274
234350
|
callTool: async (toolName2, toolArgs) => {
|
|
234275
234351
|
signal?.throwIfAborted();
|
|
234352
|
+
const serverTool = serverTools.get(toolName2);
|
|
234353
|
+
if (serverTool) {
|
|
234354
|
+
return callServerTool(parentAgentId, serverTool, toolArgs, signal);
|
|
234355
|
+
}
|
|
234276
234356
|
const toolCallId = `script-${randomUUID33()}`;
|
|
234277
234357
|
const { decision } = await manager.checkToolPermission(toolName2, toolArgs, permissionCwd, permissionModeState && { ...permissionModeState }, parentAgentId ?? undefined, toolContextId, toolCallId);
|
|
234278
234358
|
if (decision !== "allow") {
|
|
@@ -234303,12 +234383,14 @@ var SCRIPT_EXCLUDED_TOOLS;
|
|
|
234303
234383
|
var init_script_tool_scope = __esm(() => {
|
|
234304
234384
|
init_context();
|
|
234305
234385
|
init_backend2();
|
|
234386
|
+
init_client4();
|
|
234306
234387
|
init_runtime_context();
|
|
234307
234388
|
init_client_tool_serialization();
|
|
234308
234389
|
SCRIPT_EXCLUDED_TOOLS = new Set([
|
|
234309
234390
|
"AskUserQuestion",
|
|
234310
234391
|
"Workflow",
|
|
234311
|
-
"CodeMode"
|
|
234392
|
+
"CodeMode",
|
|
234393
|
+
"Monitor"
|
|
234312
234394
|
]);
|
|
234313
234395
|
});
|
|
234314
234396
|
|
|
@@ -234670,13 +234752,10 @@ async function executeWorkflow(spawner, options) {
|
|
|
234670
234752
|
return executeScript(spawner, options, meta2);
|
|
234671
234753
|
}
|
|
234672
234754
|
async function executeToolScript(options) {
|
|
234673
|
-
const run = await executeScript(undefined, options, {
|
|
234674
|
-
name: "code-mode",
|
|
234675
|
-
description: "Combine tools with JavaScript"
|
|
234676
|
-
});
|
|
234755
|
+
const run = await executeScript(undefined, options, { name: "code-mode", description: "Combine tools with JavaScript" }, options.globals);
|
|
234677
234756
|
return run.result;
|
|
234678
234757
|
}
|
|
234679
|
-
async function executeScript(spawner, options, meta2) {
|
|
234758
|
+
async function executeScript(spawner, options, meta2, extraGlobals) {
|
|
234680
234759
|
const signal = options.signal ?? new AbortController().signal;
|
|
234681
234760
|
let settled = false;
|
|
234682
234761
|
const emit = (event) => {
|
|
@@ -234952,7 +235031,8 @@ async function executeScript(spawner, options, meta2) {
|
|
|
234952
235031
|
describeTool,
|
|
234953
235032
|
...spawner ? { agent, decide, parallel, pipeline: pipeline7, phase, log: log3 } : {},
|
|
234954
235033
|
args: options.args,
|
|
234955
|
-
console: { log: log3, warn: log3, error: log3, info: log3 }
|
|
235034
|
+
console: { log: log3, warn: log3, error: log3, info: log3 },
|
|
235035
|
+
...extraGlobals
|
|
234956
235036
|
});
|
|
234957
235037
|
const wrapped = `(async () => { "use strict";
|
|
234958
235038
|
${spawner ? stripMetaExport(options.script) : options.script}
|
|
@@ -241570,6 +241650,223 @@ var init_monitor_event_stream = __esm(() => {
|
|
|
241570
241650
|
MONITOR_EVENT_BUFFER_CHARS = 1024 * 1024;
|
|
241571
241651
|
});
|
|
241572
241652
|
|
|
241653
|
+
// src/tools/impl/monitor-script.ts
|
|
241654
|
+
function rateLimitToolCalls(call2, signal) {
|
|
241655
|
+
let tokens = TOOL_CALL_BURST;
|
|
241656
|
+
let refilledAt = Date.now();
|
|
241657
|
+
return (toolName2, toolArgs) => {
|
|
241658
|
+
if (signal.aborted)
|
|
241659
|
+
return new Promise(() => {});
|
|
241660
|
+
const now2 = Date.now();
|
|
241661
|
+
const refill = Math.floor((now2 - refilledAt) / TOOL_CALL_REFILL_MS);
|
|
241662
|
+
if (refill > 0) {
|
|
241663
|
+
tokens = Math.min(TOOL_CALL_BURST, tokens + refill);
|
|
241664
|
+
refilledAt += refill * TOOL_CALL_REFILL_MS;
|
|
241665
|
+
}
|
|
241666
|
+
if (tokens === 0) {
|
|
241667
|
+
throw new Error(`Monitor script exceeded ${1000 / TOOL_CALL_REFILL_MS} tool calls per second; add sleep() between polls.`);
|
|
241668
|
+
}
|
|
241669
|
+
tokens -= 1;
|
|
241670
|
+
return call2(toolName2, toolArgs);
|
|
241671
|
+
};
|
|
241672
|
+
}
|
|
241673
|
+
function abortableSleep3(ms, signal) {
|
|
241674
|
+
const delay3 = Number(ms);
|
|
241675
|
+
if (!Number.isFinite(delay3) || delay3 < 0) {
|
|
241676
|
+
return Promise.reject(new Error("sleep(ms) needs a non-negative number."));
|
|
241677
|
+
}
|
|
241678
|
+
return new Promise((resolve34) => {
|
|
241679
|
+
if (signal.aborted)
|
|
241680
|
+
return;
|
|
241681
|
+
const onAbort = () => clearTimeout(timer);
|
|
241682
|
+
const timer = setTimeout(() => {
|
|
241683
|
+
signal.removeEventListener("abort", onAbort);
|
|
241684
|
+
resolve34();
|
|
241685
|
+
}, delay3);
|
|
241686
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
241687
|
+
});
|
|
241688
|
+
}
|
|
241689
|
+
function formatReturnValue(value) {
|
|
241690
|
+
if (value === undefined)
|
|
241691
|
+
return "The script returned without a value.";
|
|
241692
|
+
let text2;
|
|
241693
|
+
try {
|
|
241694
|
+
text2 = typeof value === "string" ? value : JSON.stringify(value);
|
|
241695
|
+
} catch (error5) {
|
|
241696
|
+
return `The script returned a value that is not JSON-serializable: ${String(error5)}`;
|
|
241697
|
+
}
|
|
241698
|
+
return text2.length <= MAX_RESULT_CHARS ? text2 : `${text2.slice(0, MAX_RESULT_CHARS)}
|
|
241699
|
+
[truncated; see the output file]`;
|
|
241700
|
+
}
|
|
241701
|
+
async function releaseToolContext(contextId) {
|
|
241702
|
+
if (!contextId || !releaseRetainedToolContext(contextId))
|
|
241703
|
+
return;
|
|
241704
|
+
const manager = await init_manager2().then(() => exports_manager);
|
|
241705
|
+
manager.releaseToolExecutionContext(contextId);
|
|
241706
|
+
}
|
|
241707
|
+
async function startScriptMonitor(args, hooks) {
|
|
241708
|
+
assertBackgroundProcessCapacity();
|
|
241709
|
+
const toolContextId = getRuntimeContext()?.toolContextId ?? null;
|
|
241710
|
+
if (toolContextId)
|
|
241711
|
+
retainToolContext(toolContextId);
|
|
241712
|
+
const controller = new AbortController;
|
|
241713
|
+
const parentAgentId = await resolveScriptParentAgentId(args).catch(async (error5) => {
|
|
241714
|
+
await releaseToolContext(toolContextId);
|
|
241715
|
+
throw error5;
|
|
241716
|
+
});
|
|
241717
|
+
const toolScope = await createScriptToolScope(parentAgentId, controller.signal).catch(async (error5) => {
|
|
241718
|
+
await releaseToolContext(toolContextId);
|
|
241719
|
+
throw error5;
|
|
241720
|
+
});
|
|
241721
|
+
const taskId = getNextMonitorId();
|
|
241722
|
+
const outputFile = createBackgroundOutputFile(taskId);
|
|
241723
|
+
const output = hooks.createOutput(outputFile);
|
|
241724
|
+
const scope = resolveNotificationScope(args.parentScope);
|
|
241725
|
+
const actingUserId = getRuntimeActingUserAttribution();
|
|
241726
|
+
const secrets2 = captureSecretRedactions(parentAgentId ?? undefined);
|
|
241727
|
+
const scrub = (text2) => scrubSecretsFromString(text2, secrets2);
|
|
241728
|
+
let timeout;
|
|
241729
|
+
const stop = (reason, note) => {
|
|
241730
|
+
if (processState.status !== "running")
|
|
241731
|
+
return;
|
|
241732
|
+
processState.completionNotificationSuppressed = true;
|
|
241733
|
+
if (note)
|
|
241734
|
+
output.append(note);
|
|
241735
|
+
hooks.markFinished(taskId, processState, "failed", null);
|
|
241736
|
+
controller.abort(new Error(reason));
|
|
241737
|
+
};
|
|
241738
|
+
const events = createMonitorEventStream({
|
|
241739
|
+
emit(event) {
|
|
241740
|
+
hooks.queueEvent({ taskId, event, scope, actingUserId, secrets: secrets2 });
|
|
241741
|
+
},
|
|
241742
|
+
stopSource() {
|
|
241743
|
+
stop("output rate too high", `
|
|
241744
|
+
[stopped: output rate too high]
|
|
241745
|
+
`);
|
|
241746
|
+
}
|
|
241747
|
+
});
|
|
241748
|
+
const firstLine = args.script.trim().split(`
|
|
241749
|
+
`)[0] ?? "";
|
|
241750
|
+
const processState = {
|
|
241751
|
+
process: {
|
|
241752
|
+
kill() {
|
|
241753
|
+
events.cancel();
|
|
241754
|
+
controller.abort(new Error("Monitor stopped via TaskStop"));
|
|
241755
|
+
}
|
|
241756
|
+
},
|
|
241757
|
+
command: `script: ${firstLine.slice(0, 80)}`,
|
|
241758
|
+
status: "running",
|
|
241759
|
+
exitCode: null,
|
|
241760
|
+
startTime: new Date,
|
|
241761
|
+
outputFile,
|
|
241762
|
+
runtimeScope: scope,
|
|
241763
|
+
actingUserId,
|
|
241764
|
+
kind: "monitor",
|
|
241765
|
+
description: args.description,
|
|
241766
|
+
monitorSource: "script",
|
|
241767
|
+
persistent: args.persistent,
|
|
241768
|
+
secrets: secrets2
|
|
241769
|
+
};
|
|
241770
|
+
backgroundProcesses.set(taskId, processState);
|
|
241771
|
+
notifyBackgroundProcessStateChanged(scope);
|
|
241772
|
+
const emit = (value) => {
|
|
241773
|
+
if (processState.status !== "running")
|
|
241774
|
+
return;
|
|
241775
|
+
const text2 = scrub(typeof value === "string" ? value : String(value));
|
|
241776
|
+
if (!text2.trim())
|
|
241777
|
+
return;
|
|
241778
|
+
const line = text2.endsWith(`
|
|
241779
|
+
`) ? text2 : `${text2}
|
|
241780
|
+
`;
|
|
241781
|
+
if (!output.append(line)) {
|
|
241782
|
+
stop("output file write failed");
|
|
241783
|
+
return;
|
|
241784
|
+
}
|
|
241785
|
+
events.onData(line);
|
|
241786
|
+
};
|
|
241787
|
+
const finish = (status, detail) => {
|
|
241788
|
+
events.finish();
|
|
241789
|
+
output.append(`
|
|
241790
|
+
[${status === "completed" ? "result" : "error"}] ${detail}
|
|
241791
|
+
`);
|
|
241792
|
+
if (processState.status !== "running")
|
|
241793
|
+
return;
|
|
241794
|
+
hooks.markFinished(taskId, processState, status, status === "completed" ? 0 : 1);
|
|
241795
|
+
const durationMs = Date.now() - (processState.startTime?.getTime() ?? 0);
|
|
241796
|
+
addToMessageQueue({
|
|
241797
|
+
kind: "task_notification",
|
|
241798
|
+
text: formatTaskNotification({
|
|
241799
|
+
taskId,
|
|
241800
|
+
status,
|
|
241801
|
+
summary: status === "completed" ? `Monitor "${args.description}" script returned` : `Monitor "${args.description}" script failed`,
|
|
241802
|
+
result: detail,
|
|
241803
|
+
outputFile,
|
|
241804
|
+
usage: { durationMs }
|
|
241805
|
+
}),
|
|
241806
|
+
...scope,
|
|
241807
|
+
...actingUserId !== undefined ? { actingUserId } : {}
|
|
241808
|
+
});
|
|
241809
|
+
};
|
|
241810
|
+
const stopped = new Promise((_resolve, reject2) => {
|
|
241811
|
+
controller.signal.addEventListener("abort", () => reject2(controller.signal.reason), { once: true });
|
|
241812
|
+
});
|
|
241813
|
+
const run = executeToolScript({
|
|
241814
|
+
...toolScope,
|
|
241815
|
+
callTool: rateLimitToolCalls(toolScope.callTool, controller.signal),
|
|
241816
|
+
maxTotalToolCalls: Number.POSITIVE_INFINITY,
|
|
241817
|
+
script: args.script,
|
|
241818
|
+
args: args.args,
|
|
241819
|
+
signal: new AbortController().signal,
|
|
241820
|
+
globals: {
|
|
241821
|
+
emit,
|
|
241822
|
+
sleep: (ms) => abortableSleep3(ms, controller.signal)
|
|
241823
|
+
},
|
|
241824
|
+
onProgress: (event) => {
|
|
241825
|
+
if (event.kind === "log")
|
|
241826
|
+
output.append(`${scrub(event.message)}
|
|
241827
|
+
`);
|
|
241828
|
+
}
|
|
241829
|
+
});
|
|
241830
|
+
run.catch(() => {});
|
|
241831
|
+
Promise.race([run, stopped]).then((value) => finish("completed", scrub(formatReturnValue(value))), (error5) => finish("failed", scrub(error5 instanceof Error ? error5.message : String(error5)))).finally(async () => {
|
|
241832
|
+
if (timeout)
|
|
241833
|
+
clearTimeout(timeout);
|
|
241834
|
+
await toolScope.cleanup();
|
|
241835
|
+
await releaseToolContext(toolContextId);
|
|
241836
|
+
});
|
|
241837
|
+
if (!args.persistent) {
|
|
241838
|
+
timeout = setTimeout(() => {
|
|
241839
|
+
if (processState.status !== "running")
|
|
241840
|
+
return;
|
|
241841
|
+
events.finish();
|
|
241842
|
+
hooks.queueEvent({
|
|
241843
|
+
taskId,
|
|
241844
|
+
event: hooks.timeoutEvent(args.timeout_ms),
|
|
241845
|
+
scope,
|
|
241846
|
+
actingUserId,
|
|
241847
|
+
secrets: secrets2
|
|
241848
|
+
});
|
|
241849
|
+
stop("timeout", `
|
|
241850
|
+
[timeout after ${args.timeout_ms}ms]
|
|
241851
|
+
`);
|
|
241852
|
+
}, args.timeout_ms);
|
|
241853
|
+
unrefTimer(timeout);
|
|
241854
|
+
}
|
|
241855
|
+
return { taskId, outputFile };
|
|
241856
|
+
}
|
|
241857
|
+
var TOOL_CALL_BURST = 120, TOOL_CALL_REFILL_MS = 500, MAX_RESULT_CHARS = 30000;
|
|
241858
|
+
var init_monitor_script = __esm(() => {
|
|
241859
|
+
init_runtime_context();
|
|
241860
|
+
init_script_tool_scope();
|
|
241861
|
+
init_secret_substitution();
|
|
241862
|
+
init_tool_context_retention();
|
|
241863
|
+
init_workflow_engine();
|
|
241864
|
+
init_message_queue_bridge();
|
|
241865
|
+
init_task_notifications();
|
|
241866
|
+
init_monitor_event_stream();
|
|
241867
|
+
init_process_manager();
|
|
241868
|
+
});
|
|
241869
|
+
|
|
241573
241870
|
// src/tools/impl/monitor.ts
|
|
241574
241871
|
import { appendFileSync as appendFileSync7, readFileSync as readFileSync37, writeFileSync as writeFileSync28 } from "node:fs";
|
|
241575
241872
|
import { WebSocket as WebSocket4 } from "ws";
|
|
@@ -241698,10 +241995,18 @@ function normalizeMonitorArgs(args) {
|
|
|
241698
241995
|
if (normalized.command !== undefined && typeof normalized.command !== "string") {
|
|
241699
241996
|
throw new Error("Monitor command must be a string");
|
|
241700
241997
|
}
|
|
241998
|
+
if (normalized.script !== undefined && typeof normalized.script !== "string") {
|
|
241999
|
+
throw new Error("Monitor script must be a string");
|
|
242000
|
+
}
|
|
242001
|
+
const hasScript = typeof normalized.script === "string" && normalized.script.trim() !== "";
|
|
241701
242002
|
const hasCommand = typeof normalized.command === "string" && normalized.command.length > 0;
|
|
241702
242003
|
const hasWebSocket = normalized.ws !== undefined && !(typeof normalized.ws === "object" && normalized.ws !== null && normalized.ws.url === "");
|
|
241703
|
-
if (Number(hasCommand) + Number(hasWebSocket) !== 1) {
|
|
241704
|
-
throw new Error("Monitor requires exactly one of command or
|
|
242004
|
+
if (Number(hasCommand) + Number(hasWebSocket) + Number(hasScript) !== 1) {
|
|
242005
|
+
throw new Error("Monitor requires exactly one of command, ws, or script");
|
|
242006
|
+
}
|
|
242007
|
+
if (!hasScript) {
|
|
242008
|
+
delete normalized.script;
|
|
242009
|
+
delete normalized.args;
|
|
241705
242010
|
}
|
|
241706
242011
|
if (!hasCommand) {
|
|
241707
242012
|
delete normalized.command;
|
|
@@ -242152,6 +242457,22 @@ function startWebSocketMonitor(args) {
|
|
|
242152
242457
|
}
|
|
242153
242458
|
return buildMonitorResult(taskId, args.persistent ? 0 : args.timeout_ms, args.persistent, outputFile);
|
|
242154
242459
|
}
|
|
242460
|
+
async function startScriptSource(args) {
|
|
242461
|
+
const { taskId, outputFile } = await startScriptMonitor({
|
|
242462
|
+
description: args.description,
|
|
242463
|
+
script: args.script,
|
|
242464
|
+
args: args.args,
|
|
242465
|
+
timeout_ms: args.timeout_ms,
|
|
242466
|
+
persistent: args.persistent,
|
|
242467
|
+
parentScope: args.parentScope
|
|
242468
|
+
}, {
|
|
242469
|
+
createOutput: (path27) => new MonitorOutputWriter(path27),
|
|
242470
|
+
queueEvent: queueMonitorEvent,
|
|
242471
|
+
markFinished: markMonitorFinished,
|
|
242472
|
+
timeoutEvent: getMonitorTimeoutEvent
|
|
242473
|
+
});
|
|
242474
|
+
return buildMonitorResult(taskId, args.persistent ? 0 : args.timeout_ms, args.persistent, outputFile);
|
|
242475
|
+
}
|
|
242155
242476
|
function backupScopeFor(taskId) {
|
|
242156
242477
|
const processState = backgroundProcesses.get(taskId);
|
|
242157
242478
|
const runtimeScope = processState?.runtimeScope;
|
|
@@ -242163,7 +242484,7 @@ async function monitor(args) {
|
|
|
242163
242484
|
args.signal?.throwIfAborted();
|
|
242164
242485
|
const normalized = normalizeMonitorArgs(args);
|
|
242165
242486
|
installMonitorProcessExitCleanup();
|
|
242166
|
-
const result3 = normalized.command !== undefined ? startCommandMonitor(normalized) : startWebSocketMonitor(normalized);
|
|
242487
|
+
const result3 = normalized.script !== undefined ? await startScriptSource(normalized) : normalized.command !== undefined ? startCommandMonitor(normalized) : startWebSocketMonitor(normalized);
|
|
242167
242488
|
if (normalized.backup_after_seconds === undefined)
|
|
242168
242489
|
return result3;
|
|
242169
242490
|
const note = await armMonitorBackup({
|
|
@@ -242207,6 +242528,7 @@ var init_monitor = __esm(async () => {
|
|
|
242207
242528
|
init_message_queue_bridge();
|
|
242208
242529
|
init_task_notifications();
|
|
242209
242530
|
init_monitor_event_stream();
|
|
242531
|
+
init_monitor_script();
|
|
242210
242532
|
init_process_manager();
|
|
242211
242533
|
init_shell_env();
|
|
242212
242534
|
init_shell_launchers();
|
|
@@ -244042,7 +244364,7 @@ class CodexAppServerClient {
|
|
|
244042
244364
|
await this.request("initialize", {
|
|
244043
244365
|
clientInfo: {
|
|
244044
244366
|
name: "letta-code",
|
|
244045
|
-
version: "0.34.
|
|
244367
|
+
version: "0.34.28"
|
|
244046
244368
|
},
|
|
244047
244369
|
capabilities: null
|
|
244048
244370
|
});
|
|
@@ -254891,7 +255213,14 @@ var init_Monitor2 = __esm(() => {
|
|
|
254891
255213
|
},
|
|
254892
255214
|
required: ["url"],
|
|
254893
255215
|
additionalProperties: false,
|
|
254894
|
-
description: "WebSocket to open. Each text frame is an event; binary frames are reported as a placeholder line. Socket close ends the watch. Cannot be combined with command."
|
|
255216
|
+
description: "WebSocket to open. Each text frame is an event; binary frames are reported as a placeholder line. Socket close ends the watch. Cannot be combined with command or script."
|
|
255217
|
+
},
|
|
255218
|
+
script: {
|
|
255219
|
+
type: "string",
|
|
255220
|
+
description: "JavaScript watch loop with CodeMode's globals plus emit(text) and sleep(ms). Cannot be combined with command or ws."
|
|
255221
|
+
},
|
|
255222
|
+
args: {
|
|
255223
|
+
description: "Optional input exposed to script as args."
|
|
254895
255224
|
}
|
|
254896
255225
|
},
|
|
254897
255226
|
required: ["description"],
|
|
@@ -443833,6 +444162,19 @@ function getInterruptApprovalsForEmission(runtime, params) {
|
|
|
443833
444162
|
}
|
|
443834
444163
|
return runtime.pendingInterruptedResults;
|
|
443835
444164
|
}
|
|
444165
|
+
function emitInterruptApprovals(socket, runtime, params) {
|
|
444166
|
+
const approvals = getInterruptApprovalsForEmission(runtime, params);
|
|
444167
|
+
if (!approvals)
|
|
444168
|
+
return;
|
|
444169
|
+
const { runId, agentId, conversationId } = params;
|
|
444170
|
+
emitToolExecutionFinishedEvents(socket, runtime, {
|
|
444171
|
+
approvals,
|
|
444172
|
+
runId,
|
|
444173
|
+
agentId,
|
|
444174
|
+
conversationId
|
|
444175
|
+
});
|
|
444176
|
+
emitInterruptToolReturnMessage(socket, runtime, approvals, runId || undefined);
|
|
444177
|
+
}
|
|
443836
444178
|
function populateInterruptQueue(runtime, input) {
|
|
443837
444179
|
const shouldPopulate = !runtime.pendingInterruptedResults || runtime.pendingInterruptedResults.length === 0 || !runtime.pendingInterruptedContext;
|
|
443838
444180
|
if (!shouldPopulate)
|
|
@@ -449194,7 +449536,8 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
|
|
|
449194
449536
|
runtime.currentToolset = preparedToolContext.toolset;
|
|
449195
449537
|
runtime.currentToolsetPreference = preparedToolContext.toolsetPreference;
|
|
449196
449538
|
runtime.currentLoadedTools = preparedToolContext.preparedToolContext.loadedToolNames;
|
|
449197
|
-
const
|
|
449539
|
+
const recoveryRecord = (deps.readInterruptedTurn ?? readInterruptedTurn)(runtime);
|
|
449540
|
+
const recoveryActingUserId = recoveryRecord?.actingUserId;
|
|
449198
449541
|
pendingApprovals = await filterStillPending(pendingApprovals);
|
|
449199
449542
|
while (pendingApprovals.length > 0) {
|
|
449200
449543
|
assertCurrentTurnLease();
|
|
@@ -449203,10 +449546,16 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
|
|
|
449203
449546
|
throw new Error("Ambiguous pending approval batch mapping during recovery");
|
|
449204
449547
|
}
|
|
449205
449548
|
rememberPendingApprovalBatchIds(runtime, pendingApprovals, recoveryBatchId);
|
|
449206
|
-
const
|
|
449549
|
+
const pendingToolCallIds = pendingApprovals.map((approval) => approval.toolCallId);
|
|
449550
|
+
const approvalResults = recoveryRecord ? recordedToolResults(recoveryRecord, pendingToolCallIds) : buildFreshDenialApprovals(pendingApprovals, STALE_APPROVAL_RECOVERY_DENIAL_REASON);
|
|
449207
449551
|
if (approvalResults.length === 0) {
|
|
449208
449552
|
return null;
|
|
449209
449553
|
}
|
|
449554
|
+
if (recoveryRecord && allRecordedResults(recoveryRecord).some((result3) => pendingToolCallIds.includes(result3.tool_call_id))) {
|
|
449555
|
+
runtime.pendingInterruptedResults = null;
|
|
449556
|
+
runtime.pendingInterruptedContext = null;
|
|
449557
|
+
runtime.pendingInterruptedToolCallIds = null;
|
|
449558
|
+
}
|
|
449210
449559
|
try {
|
|
449211
449560
|
const continuationInput = createTurnInputState([
|
|
449212
449561
|
{
|
|
@@ -449591,6 +449940,7 @@ var init_send = __esm(async () => {
|
|
|
449591
449940
|
init_constants3();
|
|
449592
449941
|
init_cwd();
|
|
449593
449942
|
init_permission_mode();
|
|
449943
|
+
init_recorded_tool_results();
|
|
449594
449944
|
init_turn_input_state();
|
|
449595
449945
|
await __promiseAll([
|
|
449596
449946
|
init_message(),
|
|
@@ -452448,6 +452798,7 @@ async function handleApprovalStop(params) {
|
|
|
452448
452798
|
if (shouldInterrupt()) {
|
|
452449
452799
|
return interruptTermination(nextTurnInput, continuationBatchId);
|
|
452450
452800
|
}
|
|
452801
|
+
params.onContinuationSendError?.(interruptTermination());
|
|
452451
452802
|
throw error5;
|
|
452452
452803
|
}
|
|
452453
452804
|
if (sendResult.kind === "terminal") {
|
|
@@ -459384,7 +459735,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
|
|
|
459384
459735
|
const conversationId = requestedConversationId ?? "default";
|
|
459385
459736
|
const turnWorkingDirectory = getConversationWorkingDirectory(runtime.listener, agentId, conversationId);
|
|
459386
459737
|
const turnPermissionModeState = getOrCreateConversationPermissionModeStateRef(runtime.listener, agentId, conversationId);
|
|
459387
|
-
let postStopApprovalRecoveryRetries = 0, deploymentRecoveryAttempts = 0, llmApiErrorRetries = 0, emptyResponseRetries = 0, chatgptPlanSwaps = 0, lastApprovalContinuationAccepted = false, activeDequeuedBatchId = dequeuedBatchId;
|
|
459738
|
+
let postStopApprovalRecoveryRetries = 0, deploymentRecoveryAttempts = 0, llmApiErrorRetries = 0, emptyResponseRetries = 0, chatgptPlanSwaps = 0, lastApprovalContinuationAccepted = false, continuationSendFailed = false, activeDequeuedBatchId = dequeuedBatchId;
|
|
459388
459739
|
const chatgptExhaustedProviders = new Set;
|
|
459389
459740
|
const turnCorrelation = existingTurnCorrelation ?? createTurnCorrelation(runtime, msg, activeDequeuedBatchId);
|
|
459390
459741
|
const msgRunIds = [];
|
|
@@ -459392,6 +459743,13 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
|
|
|
459392
459743
|
let lastExecutingToolCallIds = [];
|
|
459393
459744
|
let lastNeedsUserInputToolCallIds = [];
|
|
459394
459745
|
const durabilityOwnership = createTurnDurabilityOwnership();
|
|
459746
|
+
const recordApprovalProgress = (progress) => {
|
|
459747
|
+
({
|
|
459748
|
+
lastExecutionResults,
|
|
459749
|
+
lastExecutingToolCallIds,
|
|
459750
|
+
lastNeedsUserInputToolCallIds
|
|
459751
|
+
} = durabilityOwnership.record(progress));
|
|
459752
|
+
};
|
|
459395
459753
|
const interruptedRevisionRef = { current: initialInterruptedRevision };
|
|
459396
459754
|
durabilityOwnership.recordInput(msg);
|
|
459397
459755
|
const turnLease = existingTurnLease ?? runtime.turnLifecycle.begin({
|
|
@@ -459891,6 +460249,10 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
|
|
|
459891
460249
|
recoveryLineageId,
|
|
459892
460250
|
recoveryAuthorityStore,
|
|
459893
460251
|
buildSendOptions,
|
|
460252
|
+
onContinuationSendError: (progress) => {
|
|
460253
|
+
continuationSendFailed = true;
|
|
460254
|
+
recordApprovalProgress(progress);
|
|
460255
|
+
},
|
|
459894
460256
|
dependencies: recoveryEvidenceWriter ? { recordListenerWork: recoveryEvidenceWriter } : undefined
|
|
459895
460257
|
});
|
|
459896
460258
|
if (approvalResult.kind === "error") {
|
|
@@ -459911,11 +460273,7 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
|
|
|
459911
460273
|
activeDequeuedBatchId = approvalResult.dequeuedBatchId;
|
|
459912
460274
|
pendingNormalizationInterruptedToolCallIds = approvalResult.pendingNormalizationInterruptedToolCallIds;
|
|
459913
460275
|
turnToolContextId = approvalResult.turnToolContextId;
|
|
459914
|
-
(
|
|
459915
|
-
lastExecutionResults,
|
|
459916
|
-
lastExecutingToolCallIds,
|
|
459917
|
-
lastNeedsUserInputToolCallIds
|
|
459918
|
-
} = durabilityOwnership.record(approvalResult));
|
|
460276
|
+
recordApprovalProgress(approvalResult);
|
|
459919
460277
|
lastApprovalContinuationAccepted = approvalResult.lastApprovalContinuationAccepted;
|
|
459920
460278
|
if (approvalResult.kind === "teleport") {
|
|
459921
460279
|
const pending2 = approvalResult.pendingTeleport;
|
|
@@ -459963,29 +460321,24 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
|
|
|
459963
460321
|
context: "listener_turn_processing",
|
|
459964
460322
|
runId: runtime.activeRunId || msgRunIds[msgRunIds.length - 1]
|
|
459965
460323
|
});
|
|
460324
|
+
const canParkUnsentResults = runtime.turnLifecycle.isCurrent(turnLease) && !lastApprovalContinuationAccepted;
|
|
460325
|
+
if (canParkUnsentResults && (turnAbortSignal.aborted || continuationSendFailed)) {
|
|
460326
|
+
populateInterruptQueue(runtime, {
|
|
460327
|
+
lastExecutionResults,
|
|
460328
|
+
lastExecutingToolCallIds,
|
|
460329
|
+
lastNeedsUserInputToolCallIds,
|
|
460330
|
+
agentId,
|
|
460331
|
+
conversationId
|
|
460332
|
+
});
|
|
460333
|
+
}
|
|
459966
460334
|
if (turnAbortSignal.aborted) {
|
|
459967
|
-
if (
|
|
459968
|
-
|
|
460335
|
+
if (canParkUnsentResults) {
|
|
460336
|
+
emitInterruptApprovals(socket, runtime, {
|
|
459969
460337
|
lastExecutionResults,
|
|
459970
|
-
|
|
459971
|
-
lastNeedsUserInputToolCallIds,
|
|
460338
|
+
runId: runtime.activeRunId || msgRunIds[msgRunIds.length - 1],
|
|
459972
460339
|
agentId,
|
|
459973
460340
|
conversationId
|
|
459974
460341
|
});
|
|
459975
|
-
const approvalsForEmission = getInterruptApprovalsForEmission(runtime, {
|
|
459976
|
-
lastExecutionResults,
|
|
459977
|
-
agentId,
|
|
459978
|
-
conversationId
|
|
459979
|
-
});
|
|
459980
|
-
if (approvalsForEmission) {
|
|
459981
|
-
emitToolExecutionFinishedEvents(socket, runtime, {
|
|
459982
|
-
approvals: approvalsForEmission,
|
|
459983
|
-
runId: runtime.activeRunId || msgRunIds[msgRunIds.length - 1],
|
|
459984
|
-
agentId,
|
|
459985
|
-
conversationId
|
|
459986
|
-
});
|
|
459987
|
-
emitInterruptToolReturnMessage(socket, runtime, approvalsForEmission, runtime.activeRunId || msgRunIds[msgRunIds.length - 1] || undefined);
|
|
459988
|
-
}
|
|
459989
460342
|
}
|
|
459990
460343
|
finishTurn({
|
|
459991
460344
|
stopReason: "cancelled",
|
|
@@ -538684,4 +539037,4 @@ function registerBunOAuthFlows() {
|
|
|
538684
539037
|
registerBunOAuthFlows();
|
|
538685
539038
|
await init_src5().then(() => exports_src2);
|
|
538686
539039
|
|
|
538687
|
-
//# debugId=
|
|
539040
|
+
//# debugId=5934D32E04CC3ADA64756E2164756E21
|