@letta-ai/letta-code 0.33.4 → 0.33.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js CHANGED
@@ -5653,7 +5653,7 @@ var package_default;
5653
5653
  var init_package = __esm(() => {
5654
5654
  package_default = {
5655
5655
  name: "@letta-ai/letta-code",
5656
- version: "0.33.4",
5656
+ version: "0.33.5",
5657
5657
  lettaStartupLogProtocol: 1,
5658
5658
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5659
5659
  type: "module",
@@ -5785,7 +5785,7 @@ var init_package = __esm(() => {
5785
5785
  dependencies: {
5786
5786
  "@earendil-works/pi-ai": "^0.87.1",
5787
5787
  "@janhapke/sharp-electron": "0.35.3-electron.1",
5788
- "@letta-ai/letta-agent-sdk": "^0.8.17",
5788
+ "@letta-ai/letta-agent-sdk": "0.8.21",
5789
5789
  "@letta-ai/letta-client": "^1.10.2",
5790
5790
  "@letta-ai/trajectory": "0.2.0",
5791
5791
  "@modelcontextprotocol/sdk": "1.30.0",
@@ -111416,7 +111416,6 @@ var init_names = __esm(() => {
111416
111416
  "Songbird",
111417
111417
  "Slider",
111418
111418
  "Aguilar",
111419
- "Dogmeat",
111420
111419
  "Codsworth",
111421
111420
  "Nick Valentine",
111422
111421
  "Yes Man",
@@ -203018,40 +203017,31 @@ Pass a URL ending at the PR number, such as \`https://github.com/owner/repo/pull
203018
203017
  var init_WatchPR = () => {};
203019
203018
 
203020
203019
  // src/tools/descriptions/Workflow.md
203021
- var Workflow_default = `Execute a workflow script that orchestrates multiple subagents deterministically. Use for multi-step orchestration where control flow should be deterministic (loops, conditionals, fan-out) rather than model-driven.
203020
+ var Workflow_default = `Execute a workflow script that orchestrates multiple subagents deterministically (loops, conditionals, fan-out) rather than model-driven.
203022
203021
 
203023
- Workflows run in the background — this tool validates the script and returns immediately with a task ID (\`workflow_N\`) and output file path; a <task-notification> arrives when the workflow completes, carrying the script's return value. Do not poll or sleep for it: keep working or end your turn, and never fabricate the result before the notification arrives. Read the output file only when you need interim progress; TaskStop aborts the run.
203022
+ Workflows run in the background: this tool returns immediately with a task ID, and a <task-notification> carrying the script's return value arrives when the workflow completes. Do not poll or sleep for it, and never fabricate the result before it arrives. TaskStop aborts the run.
203024
203023
 
203025
- ONLY call this tool when the user has explicitly opted into multi-agent orchestration. Workflows can spawn dozens of subagent sessions and cost real money; the user must request that scale, not have it inferred. Explicit opt-in means: the user directly asked for a workflow or multi-agent orchestration in their own words ("use a workflow", "fan out agents", "orchestrate this with subagents"), asked for a comprehensive audit/sweep at a scale that plainly requires it, or invoked a skill whose instructions call this tool. For any other task — even one that would benefit from parallelism — describe what a workflow could do and ask first.
203024
+ ONLY call this tool when the user has explicitly opted into multi-agent orchestration — they asked for a workflow or multi-agent orchestration in their own words ("use a workflow", "fan out agents"), or invoked a skill whose instructions call this tool. Workflows can spawn many subagents and cost real money. For any other task, even one that would benefit from parallelism, describe what a workflow could do and ask first.
203026
203025
 
203027
- Every script must begin with \`export const meta = {...}\`: a PURE LITERAL (no variables, calls, or interpolation) giving the workflow's \`name\` (kebab-case), a one-line \`description\`, and optionally \`phases\` — one \`{ title, detail? }\` per phase() call. Scripts are plain JavaScript, not TypeScript. The script runs in the CLI process with the CLI's privileges and the user approves it by reading it, so keep it to orchestration (deciding what runs, combining results); reading, searching, and writing belong in subagents, where the tool allowlist applies.
203028
-
203029
- The canonical multi-stage pattern — pipeline by default, each item verifies as soon as its review completes:
203026
+ Scripts are plain JavaScript and must begin with \`export const meta = {...}\`, a pure literal (no variables, calls, or interpolation). Pass the work list via \`args\`. Prefer \`pipeline()\` so each item moves to its next stage as soon as it is ready; \`agent()\` resolves to \`null\` on failure, so guard and filter:
203030
203027
 
203031
203028
  export const meta = {
203032
- name: 'review-changes',
203033
- description: 'Review changed files across dimensions, verify each finding',
203029
+ name: 'review-files',
203030
+ description: 'Review each file for bugs, verify each finding',
203034
203031
  phases: [{ title: 'Review' }, { title: 'Verify' }],
203035
203032
  }
203036
- const DIMENSIONS = [{key: 'bugs', prompt: '...'}, {key: 'perf', prompt: '...'}]
203037
- const findingsSchema = {type: 'object', properties: {findings: {type: 'array', items: {type: 'object', properties: {file: {type: 'string'}, summary: {type: 'string'}}, required: ['file', 'summary']}}}, required: ['findings']}
203038
- const verdictSchema = {type: 'object', properties: {real: {type: 'boolean'}, why: {type: 'string'}}, required: ['real', 'why']}
203033
+ const findings = {type: 'object', properties: {bugs: {type: 'array', items: {type: 'string'}}}, required: ['bugs']}
203034
+ const verdict = {type: 'object', properties: {real: {type: 'boolean'}}, required: ['real']}
203039
203035
  const results = await pipeline(
203040
- DIMENSIONS,
203041
- d => agent(d.prompt, {label: \`review:\${d.key}\`, phase: 'Review', schema: findingsSchema}),
203042
- review => review ? parallel(review.findings.map(f => () =>
203043
- agent(\`Adversarially verify: \${f.summary}\`, {label: \`verify:\${f.file}\`, phase: 'Verify', schema: verdictSchema})
203044
- .then(v => ({...f, verdict: v})))) : []
203036
+ args.files,
203037
+ f => agent(\`Review \${f} for bugs.\`, { phase: 'Review', schema: findings }),
203038
+ (review, f) => parallel((review?.bugs ?? []).map(bug => () =>
203039
+ agent(\`Is this a real bug in \${f}? \${bug}\`, { phase: 'Verify', schema: verdict })
203040
+ .then(v => v?.real ? { file: f, bug } : null))),
203045
203041
  )
203046
- return { confirmed: results.filter(Boolean).flat().filter(Boolean).filter(f => f.verdict?.real) }
203047
-
203048
- Script hooks: agent(prompt, opts?) spawns one subagent and resolves to final text. Prefer opts.schema (JSON Schema) for a validated object; invalid/missing results retry and then resolve to null with the validation error in the journal. opts.json still parses without validating; schema wins if both are supplied. Options: label, phase, schema, json, model, effort, allowedTools, systemPrompt, timeoutMs, maxToolCalls. pipeline(items, ...stages) runs each item through all stages independently with NO barrier between stages; each stage receives (prevResult, originalItem, index); a throwing stage drops that item to null. parallel(thunks) runs zero-arg functions concurrently and IS a barrier — use only when a stage genuinely needs all prior results together. phase(title) groups subsequent agents in progress output (inside concurrent stages use opts.phase instead — the global phase races). log(message) emits a progress line. args is the invocation's args input, verbatim.
203049
-
203050
- Subagents run in isolated agent-free ephemeral conversations with read-only tools by default (Read, Grep, Glob) and no access to your memory or conversation — put ALL context they need in the prompt. Their model defaults to the invoking conversation's model; opts.model or the tool's model input accept any handle or alias from \`letta model list\`. They are told their final text IS the return value, so they return raw data. Concurrency is capped (excess agent() calls queue); a lifetime cap of 1000 agents per run is the runaway-loop backstop. Each subagent is also guarded: a 10-minute default timeout (override with opts.timeoutMs), at most 1000 unique tool calls by default (override with the positive safe integer opts.maxToolCalls), and a stop after three identical consecutive tool calls — a guarded call resolves to null and the journal records which guard fired.
203042
+ return results.filter(Boolean).flat().filter(Boolean)
203051
203043
 
203052
- Every run persists its script and a journal of each subagent's outcome under ~/.letta/workflows/executions/<id>/ (the paths are in the tool result). Before diagnosing an empty or unexpected result, read that run's journal.jsonl; it records each agent's actual return value. Workflow subagents require the API backend.
203053
-
203054
- Before authoring a script, load the \`workflow-authoring\` skill — the workflow authoring reference: script API and gotchas, pipeline-vs-barrier rules, quality patterns (adversarial verify, judge panel, loop-until-dry), and worked examples.
203044
+ Before authoring a script, load the \`workflow-authoring\` skill — the script API, pipeline-vs-barrier rules, quality patterns, worked examples, and how to diagnose a run.
203055
203045
  `;
203056
203046
  var init_Workflow = () => {};
203057
203047
 
@@ -213029,7 +213019,7 @@ class CodexAppServerClient {
213029
213019
  await this.request("initialize", {
213030
213020
  clientInfo: {
213031
213021
  name: "letta-code",
213032
- version: "0.33.4"
213022
+ version: "0.33.5"
213033
213023
  },
213034
213024
  capabilities: null
213035
213025
  });
@@ -220357,6 +220347,7 @@ function registerWorkflowExecution(params) {
220357
220347
  startedAt: params.startedAt ?? Date.now(),
220358
220348
  phases: (params.meta.phases ?? []).map((p2) => p2.title),
220359
220349
  agents: new Map,
220350
+ decisionTokens: 0,
220360
220351
  logs: []
220361
220352
  });
220362
220353
  notify();
@@ -220375,6 +220366,9 @@ function recordWorkflowProgress(taskId, event) {
220375
220366
  if (record4.logs.length > MAX_LOG_LINES)
220376
220367
  record4.logs.shift();
220377
220368
  break;
220369
+ case "decision_usage":
220370
+ record4.decisionTokens += event.totalTokens;
220371
+ break;
220378
220372
  case "agent": {
220379
220373
  const previous = record4.agents.get(event.callIndex);
220380
220374
  const phase = event.phase ?? previous?.phase ?? null;
@@ -220446,7 +220440,7 @@ function snapshot(record4, now2) {
220446
220440
  agentsDone: agents.filter((a2) => a2.status === "done").length,
220447
220441
  agentsFailed: agents.filter((a2) => a2.status === "error").length,
220448
220442
  agentsRunning: agents.filter((a2) => a2.status === "running").length,
220449
- totalTokens: agents.reduce((sum2, a2) => sum2 + (a2.totalTokens ?? 0), 0),
220443
+ totalTokens: record4.decisionTokens + agents.reduce((sum2, a2) => sum2 + (a2.totalTokens ?? 0), 0),
220450
220444
  phases: phaseOrder.map((title) => ({
220451
220445
  title,
220452
220446
  agents: byPhase.get(title) ?? []
@@ -220647,6 +220641,22 @@ import { existsSync as existsSync45, readFileSync as readFileSync35 } from "node
220647
220641
  import { createRequire as createRequire6 } from "node:module";
220648
220642
  import { dirname as dirname26, join as join64 } from "node:path";
220649
220643
  import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL3 } from "node:url";
220644
+ function supportsPublishedResume(version2) {
220645
+ if (!version2 || !MIN_RESUME_SDK_VERSION)
220646
+ return false;
220647
+ const match2 = /^(\d+)\.(\d+)\.(\d+)$/.exec(version2);
220648
+ const minimum3 = MIN_RESUME_SDK_VERSION.split(".").map(Number);
220649
+ if (!match2)
220650
+ return false;
220651
+ const parts = match2.slice(1).map(Number);
220652
+ for (let index = 0;index < parts.length; index++) {
220653
+ const part = parts[index];
220654
+ const required5 = minimum3[index];
220655
+ if (part !== required5)
220656
+ return (part ?? 0) > (required5 ?? 0);
220657
+ }
220658
+ return true;
220659
+ }
220650
220660
  function preferRunningCliForSubagents() {
220651
220661
  if (process.env.LETTA_CLI_PATH)
220652
220662
  return;
@@ -220655,6 +220665,17 @@ function preferRunningCliForSubagents() {
220655
220665
  process.env.LETTA_CLI_PATH = entry;
220656
220666
  }
220657
220667
  }
220668
+ function packageVersion(entry) {
220669
+ for (let dir = dirname26(entry);; dir = dirname26(dir)) {
220670
+ try {
220671
+ const manifest = JSON.parse(readFileSync35(join64(dir, "package.json"), "utf8"));
220672
+ if (manifest.name === SDK_PACKAGE)
220673
+ return manifest.version;
220674
+ } catch {}
220675
+ if (dir === dirname26(dir))
220676
+ return;
220677
+ }
220678
+ }
220658
220679
  function packageEntry(packageDir) {
220659
220680
  try {
220660
220681
  const manifest = JSON.parse(readFileSync35(join64(packageDir, "package.json"), "utf8"));
@@ -220722,6 +220743,7 @@ async function loadAgentSdk() {
220722
220743
  continue;
220723
220744
  }
220724
220745
  return {
220746
+ supportsAgentFreeResume: process.env.LETTA_WORKFLOW_AGENT_FREE_RESUME === "1" && Boolean(envPath) && specifier === specifiers[0] || specifier.startsWith("file:") && supportsPublishedResume(packageVersion(fileURLToPath7(specifier))),
220725
220747
  createLocalClient: () => {
220726
220748
  const client = new sdk.LettaAgentClient({
220727
220749
  backend: "local",
@@ -220751,12 +220773,26 @@ async function loadAgentSdk() {
220751
220773
  ${attempts.map((a2) => ` tried ${a2}`).join(`
220752
220774
  `)}`);
220753
220775
  }
220754
- var SDK_PACKAGE;
220776
+ var SDK_PACKAGE, MIN_RESUME_SDK_VERSION = "0.8.20";
220755
220777
  var init_sdk_loader = __esm(() => {
220756
220778
  SDK_PACKAGE = ["@letta-ai", "letta-agent-sdk"].join("/");
220757
220779
  });
220758
220780
 
220759
220781
  // src/tools/workflow/sdk-spawner.ts
220782
+ async function latestWorkerRun(conversationId) {
220783
+ const client = await getClient();
220784
+ const messages = await client.conversations.messages.list(conversationId, {
220785
+ limit: 100,
220786
+ order: "desc"
220787
+ });
220788
+ const runId = messages.getPaginatedItems().map((message) => message.run_id).find((id2) => typeof id2 === "string" && id2.startsWith("run-"));
220789
+ if (!runId)
220790
+ return null;
220791
+ return client.runs.retrieve(runId);
220792
+ }
220793
+ async function retrieveWorkerConversation(id2) {
220794
+ return apiRequest("GET", `/v1/conversations/${encodeURIComponent(id2)}`);
220795
+ }
220760
220796
  function parseJsonReply(text2) {
220761
220797
  const trimmed = text2.trim();
220762
220798
  const fenced = /^```(?:json)?\s*([\s\S]*?)\s*```$/.exec(trimmed);
@@ -220842,14 +220878,20 @@ function usageTokensFromEvent(event) {
220842
220878
  const total = event.total_tokens;
220843
220879
  return typeof total === "number" && Number.isFinite(total) ? total : undefined;
220844
220880
  }
220845
- async function drainTurn(query3, usage, stop, maxToolCalls, onUsage) {
220881
+ async function drainTurn(query3, usage, stop, maxToolCalls, onUsage, onStarted) {
220846
220882
  let assistantText2 = "";
220847
220883
  let resultText;
220848
220884
  let success = false;
220849
220885
  let error4;
220850
220886
  let structuredOutput;
220887
+ let startedNotified = false;
220851
220888
  const guard = createToolCallGuard(maxToolCalls);
220852
220889
  for await (const message of query3) {
220890
+ const id2 = query3.conversationId ?? message.conversationId;
220891
+ if (!startedNotified && id2) {
220892
+ startedNotified = true;
220893
+ onStarted?.(id2);
220894
+ }
220853
220895
  if (message.type === "assistant")
220854
220896
  assistantText2 += message.content ?? "";
220855
220897
  if (message.type === "stream_event") {
@@ -220900,16 +220942,44 @@ function buildQueryOptions(options, model, config, callIndex) {
220900
220942
  name: options.label ?? `Workflow worker ${callIndex + 1}`,
220901
220943
  system: system2,
220902
220944
  permissionMode: "unrestricted",
220903
- allowedTools: options.allowedTools ?? config.allowedTools ?? DEFAULT_ALLOWED_TOOLS,
220945
+ allowedTools: options.allowedTools ?? (options.conversationId ? [] : config.allowedTools ?? DEFAULT_ALLOWED_TOOLS),
220904
220946
  skillSources: [],
220905
220947
  ...config.cwd ? { cwd: config.cwd } : {},
220948
+ disableMemoryGuard: true,
220906
220949
  ...options.effort ? { modelSettings: { reasoning_effort: options.effort } } : {}
220907
220950
  };
220908
220951
  }
220909
220952
  function createSdkSpawner(client, config) {
220910
220953
  return async (request, signal, hooks) => {
220911
220954
  const { prompt, options } = request;
220912
- const model = options.model ? config.resolveModel?.(options.model) ?? null : config.model;
220955
+ const resumeId = options.conversationId;
220956
+ if (resumeId !== undefined && !/^conv-[A-Za-z0-9_-]+$/.test(resumeId)) {
220957
+ throw new Error("agent() conversationId must be a conv-... ID.");
220958
+ }
220959
+ if (resumeId && !config.supportsAgentFreeResume) {
220960
+ throw new Error("Workflow worker continuation requires an Agent SDK release with agent-free query() resume support.");
220961
+ }
220962
+ const persisted = resumeId ? await (config.retrieveConversation ?? retrieveWorkerConversation)(resumeId) : null;
220963
+ if (persisted && (persisted.agent_id !== null || persisted.parent_agent_id !== config.parentAgentId)) {
220964
+ throw new Error("Worker conversation is not agent-free or belongs to another parent agent.");
220965
+ }
220966
+ if (persisted && resumeId) {
220967
+ const latest = await (config.latestRun ?? latestWorkerRun)(resumeId);
220968
+ if (!latest || latest.conversation_id !== resumeId || !["completed", "failed", "cancelled"].includes(latest.status ?? "")) {
220969
+ throw new Error("Cannot confirm worker's latest run is terminal; reconcile it before continuing.");
220970
+ }
220971
+ }
220972
+ if (persisted && (!persisted.model || typeof persisted.model !== "string")) {
220973
+ throw new Error("Worker conversation has no persisted model; cannot continue safely.");
220974
+ }
220975
+ const selectedModel = options.model ? config.resolveModel?.(options.model) ?? null : config.model;
220976
+ if (persisted && options.systemPrompt !== undefined) {
220977
+ throw new Error("agent() continuation cannot override the worker system prompt.");
220978
+ }
220979
+ if (persisted && options.model && selectedModel !== persisted.model) {
220980
+ throw new Error("agent() continuation model must match the persisted worker model.");
220981
+ }
220982
+ const model = persisted ? persisted.model : selectedModel;
220913
220983
  if (!model) {
220914
220984
  return {
220915
220985
  value: null,
@@ -220928,8 +220998,15 @@ function createSdkSpawner(client, config) {
220928
220998
  const usage = {};
220929
220999
  const query3 = client.query({
220930
221000
  prompt,
220931
- options: buildQueryOptions(options, model, config, request.callIndex)
221001
+ options: {
221002
+ ...buildQueryOptions(options, model, config, request.callIndex),
221003
+ ...resumeId ? { conversationId: resumeId } : {}
221004
+ }
220932
221005
  });
221006
+ if (resumeId && query3.conversationId && query3.conversationId !== resumeId) {
221007
+ query3.close();
221008
+ throw new Error("SDK resumed an unexpected worker conversation; refusing continuation.");
221009
+ }
220933
221010
  let settleStopped;
220934
221011
  const stopped = new Promise((resolve30) => {
220935
221012
  settleStopped = resolve30;
@@ -220959,44 +221036,68 @@ function createSdkSpawner(client, config) {
220959
221036
  drainTurn(query3, usage, stop, options.maxToolCalls ?? DEFAULT_MAX_SUBAGENT_TOOL_CALLS, (totalTokens) => {
220960
221037
  if (!finished2)
220961
221038
  hooks?.onUsage?.(totalTokens);
221039
+ }, (id2) => {
221040
+ if (resumeId && id2 !== resumeId) {
221041
+ stop("SDK resumed an unexpected worker conversation");
221042
+ } else if (!finished2) {
221043
+ hooks?.onStarted?.(id2);
221044
+ }
220962
221045
  }),
220963
221046
  stopped
220964
221047
  ]);
221048
+ if (resumeId && query3.conversationId !== resumeId) {
221049
+ throw new Error("SDK did not resume the requested worker conversation.");
221050
+ }
220965
221051
  const stats = {
220966
221052
  durationMs: Date.now() - startedAt,
220967
221053
  ...turn.totalTokens !== undefined ? { totalTokens: turn.totalTokens } : {},
220968
221054
  ...query3.conversationId ? { conversationId: query3.conversationId } : {}
220969
221055
  };
220970
221056
  if (!turn.success) {
220971
- return {
221057
+ const outcome2 = {
220972
221058
  value: null,
220973
221059
  failed: true,
220974
221060
  error: turn.error ?? "subagent turn failed",
220975
221061
  ...stats
220976
221062
  };
221063
+ return outcome2;
220977
221064
  }
220978
221065
  if (options.schema) {
220979
- return { value: turn.structuredOutput, failed: false, ...stats };
221066
+ const outcome2 = {
221067
+ value: turn.structuredOutput,
221068
+ failed: false,
221069
+ ...stats
221070
+ };
221071
+ return outcome2;
220980
221072
  }
220981
221073
  if (options.json) {
220982
221074
  try {
220983
- return {
221075
+ const outcome2 = {
220984
221076
  value: parseJsonReply(turn.finalText),
220985
221077
  failed: false,
220986
221078
  ...stats
220987
221079
  };
221080
+ return outcome2;
220988
221081
  } catch {
220989
- return {
221082
+ const outcome2 = {
220990
221083
  value: null,
220991
221084
  failed: true,
220992
221085
  error: `Subagent reply was not valid JSON: ${turn.finalText.slice(0, 200)}`,
220993
221086
  ...stats
220994
221087
  };
221088
+ return outcome2;
220995
221089
  }
220996
221090
  }
220997
- return { value: turn.finalText, failed: false, ...stats };
221091
+ const outcome = { value: turn.finalText, failed: false, ...stats };
221092
+ return outcome;
220998
221093
  } catch (error4) {
220999
- return { value: null, failed: true, error: String(error4) };
221094
+ const outcome = {
221095
+ value: null,
221096
+ failed: true,
221097
+ error: String(error4),
221098
+ ...query3.conversationId || resumeId ? { conversationId: query3.conversationId ?? resumeId } : {}
221099
+ };
221100
+ return outcome;
221000
221101
  } finally {
221001
221102
  finished2 = true;
221002
221103
  clearTimeout(timeout);
@@ -221007,10 +221108,140 @@ function createSdkSpawner(client, config) {
221007
221108
  }
221008
221109
  var DEFAULT_ALLOWED_TOOLS, DEFAULT_SUBAGENT_TIMEOUT_MS, DEFAULT_MAX_SUBAGENT_TOOL_CALLS = 1000, MAX_IDENTICAL_TOOL_CALLS = 3, SUBAGENT_PREAMBLE = `You are a subagent inside a deterministic workflow. You are not talking to a human: your final output is consumed by a script. Return raw data with no preamble, no markdown framing, and no questions.`, JSON_PREAMBLE = `Your final message must be a single JSON value and nothing else: no prose, no markdown code fence.`;
221009
221110
  var init_sdk_spawner = __esm(() => {
221111
+ init_client4();
221112
+ init_request();
221010
221113
  DEFAULT_ALLOWED_TOOLS = ["Read", "Grep", "Glob"];
221011
221114
  DEFAULT_SUBAGENT_TIMEOUT_MS = 10 * 60 * 1000;
221012
221115
  });
221013
221116
 
221117
+ // src/tools/workflow/decide.ts
221118
+ function isRecord9(value) {
221119
+ return value !== null && typeof value === "object" && !Array.isArray(value);
221120
+ }
221121
+ function isFiniteNumber2(value) {
221122
+ return typeof value === "number" && Number.isFinite(value);
221123
+ }
221124
+ function validateRequest(input) {
221125
+ if (!isRecord9(input))
221126
+ throw new Error("decide() requires a request object.");
221127
+ const { state, questions, provider, session_id, trace, user } = input;
221128
+ if (Object.hasOwn(input, "model"))
221129
+ throw new Error("decide() does not accept a model option.");
221130
+ if (!(typeof state === "string" || isRecord9(state) || Array.isArray(state))) {
221131
+ throw new Error("decide() state must be a string, object, or array.");
221132
+ }
221133
+ if (!isRecord9(questions) || Object.keys(questions).length === 0) {
221134
+ throw new Error("decide() questions must be a non-empty object.");
221135
+ }
221136
+ for (const [id2, question] of Object.entries(questions)) {
221137
+ if (!isRecord9(question) || typeof question.instructions !== "string" || !question.instructions.trim()) {
221138
+ throw new Error(`decide() question ${id2} requires instructions.`);
221139
+ }
221140
+ if (question.type === "choice") {
221141
+ if (!isRecord9(question.criteria) || Object.keys(question.criteria).length === 0 || Object.keys(question.criteria).length > 255 || !Object.values(question.criteria).every((value) => typeof value === "string")) {
221142
+ throw new Error(`decide() choice ${id2} requires a non-empty criteria map (at most 255 options).`);
221143
+ }
221144
+ } else if (question.type === "score") {
221145
+ if (!Array.isArray(question.criteria) || question.criteria.length === 0 || !question.criteria.every((value) => typeof value === "string")) {
221146
+ throw new Error(`decide() score ${id2} requires a non-empty criteria array.`);
221147
+ }
221148
+ } else if (question.type !== "noul") {
221149
+ throw new Error(`decide() question ${id2} has an unsupported type.`);
221150
+ }
221151
+ }
221152
+ if (provider !== undefined && !isRecord9(provider))
221153
+ throw new Error("decide() provider must be an object.");
221154
+ if (isRecord9(provider) && provider.allow_fallbacks === true) {
221155
+ throw new Error("decide() does not allow model/provider fallbacks.");
221156
+ }
221157
+ if (session_id !== undefined && (typeof session_id !== "string" || session_id.length > 256)) {
221158
+ throw new Error("decide() session_id must be a string of at most 256 characters.");
221159
+ }
221160
+ if (trace !== undefined && !isRecord9(trace))
221161
+ throw new Error("decide() trace must be an object.");
221162
+ if (user !== undefined && typeof user !== "string")
221163
+ throw new Error("decide() user must be a string.");
221164
+ return input;
221165
+ }
221166
+ function validProbabilityMap(value) {
221167
+ return isRecord9(value) && Object.values(value).every(isFiniteNumber2);
221168
+ }
221169
+ function validAnswer(answer, question) {
221170
+ if (!isRecord9(answer) || answer.type !== question.type)
221171
+ return false;
221172
+ if (answer.type === "choice") {
221173
+ return typeof answer.choice === "string" && isRecord9(question.criteria) && Object.hasOwn(question.criteria, answer.choice) && (answer.probabilities === undefined || validProbabilityMap(answer.probabilities)) && (answer.confidence === undefined || isFiniteNumber2(answer.confidence));
221174
+ }
221175
+ if (answer.type === "score") {
221176
+ return isFiniteNumber2(answer.score) && isRecord9(answer.legend) && Object.values(answer.legend).every((value) => typeof value === "string") && (answer.probabilities === undefined || validProbabilityMap(answer.probabilities)) && (answer.confidence === undefined || isFiniteNumber2(answer.confidence));
221177
+ }
221178
+ return answer.type === "noul" && isFiniteNumber2(answer.noul) && answer.noul >= 0 && answer.noul <= 1;
221179
+ }
221180
+ function validateResponse(response, request) {
221181
+ if (!isRecord9(response) || typeof response.model !== "string" || typeof response.id !== "string" || typeof response.provider !== "string" || !isRecord9(response.usage) || !isFiniteNumber2(response.usage.input_tokens) || !isFiniteNumber2(response.usage.output_tokens) || !isFiniteNumber2(response.usage.cost) || !isRecord9(response.answers)) {
221182
+ throw new Error("decide() received a malformed response.");
221183
+ }
221184
+ const answers = response.answers;
221185
+ const questionIds = Object.keys(request.questions);
221186
+ if (Object.keys(answers).length !== questionIds.length || questionIds.some((id2) => !validAnswer(answers[id2], request.questions[id2]))) {
221187
+ throw new InvalidDecisionAnswerError;
221188
+ }
221189
+ return {
221190
+ ...response,
221191
+ answers: Object.fromEntries(questionIds.map((id2) => [
221192
+ id2,
221193
+ { ...answers[id2], calibrated: true }
221194
+ ]))
221195
+ };
221196
+ }
221197
+ async function submitWorkflowDecision(input, signal, onResult) {
221198
+ const request = validateRequest(input);
221199
+ const body3 = {
221200
+ ...request,
221201
+ model: DEFAULT_JEV_MODEL,
221202
+ provider: { ...request.provider, allow_fallbacks: false }
221203
+ };
221204
+ for (let attempt2 = 0;attempt2 < 2; attempt2++) {
221205
+ if (signal.aborted)
221206
+ throw new Error("Workflow aborted.");
221207
+ const response = await apiRequest("POST", "/v1/alpha/decisions", body3, { signal: AbortSignal.any([signal, AbortSignal.timeout(35000)]) });
221208
+ try {
221209
+ const result3 = validateResponse(response, request);
221210
+ onResult?.({
221211
+ model: result3.model,
221212
+ cost: result3.usage.cost,
221213
+ calibrated: true,
221214
+ valid: true,
221215
+ totalTokens: result3.usage.input_tokens + result3.usage.output_tokens
221216
+ });
221217
+ return result3;
221218
+ } catch (error4) {
221219
+ if (!(error4 instanceof InvalidDecisionAnswerError))
221220
+ throw error4;
221221
+ const usage = response.usage;
221222
+ onResult?.({
221223
+ model: response.model,
221224
+ cost: usage.cost,
221225
+ calibrated: false,
221226
+ valid: false,
221227
+ totalTokens: usage.input_tokens + usage.output_tokens
221228
+ });
221229
+ if (attempt2 === 1)
221230
+ return null;
221231
+ }
221232
+ }
221233
+ return null;
221234
+ }
221235
+ var DEFAULT_JEV_MODEL = "~typesafe/jev-latest", InvalidDecisionAnswerError;
221236
+ var init_decide = __esm(() => {
221237
+ init_request();
221238
+ InvalidDecisionAnswerError = class InvalidDecisionAnswerError extends Error {
221239
+ constructor() {
221240
+ super("decide() received invalid answers for the requested questions.");
221241
+ }
221242
+ };
221243
+ });
221244
+
221014
221245
  // src/tools/workflow/workflow-engine.ts
221015
221246
  import vm2 from "node:vm";
221016
221247
 
@@ -221049,18 +221280,57 @@ async function executeWorkflow(spawner, options) {
221049
221280
  };
221050
221281
  const inFlight = new Set;
221051
221282
  const maxTotalAgents = options.maxTotalAgents ?? DEFAULT_MAX_TOTAL_AGENTS;
221283
+ const maxTotalDecisions = options.maxTotalDecisions ?? DEFAULT_MAX_TOTAL_DECISIONS;
221052
221284
  const semaphore = new Semaphore(options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT);
221053
221285
  let currentPhase = null;
221054
221286
  let callCounter = 0;
221287
+ let decisionCounter = 0;
221055
221288
  let agentsSpawned = 0;
221056
221289
  let totalTokens = 0;
221057
- function agent(prompt, callOptions) {
221058
- const pending2 = callAgent(prompt, callOptions);
221290
+ function trackCall(pending2) {
221059
221291
  pending2.catch(() => {});
221060
221292
  inFlight.add(pending2);
221061
221293
  pending2.finally(() => inFlight.delete(pending2)).catch(() => {});
221062
221294
  return pending2;
221063
221295
  }
221296
+ function agent(prompt, callOptions) {
221297
+ return trackCall(callAgent(prompt, callOptions));
221298
+ }
221299
+ function decide(state, questions, callOptions) {
221300
+ return trackCall(callDecision(state, questions, callOptions));
221301
+ }
221302
+ async function callDecision(state, questions, callOptions) {
221303
+ if (signal.aborted)
221304
+ throw new Error("Workflow aborted.");
221305
+ if (decisionCounter >= maxTotalDecisions) {
221306
+ throw new Error(`Lifetime decision cap of ${maxTotalDecisions} reached.`);
221307
+ }
221308
+ if (callOptions !== undefined && (!callOptions || typeof callOptions !== "object" || Array.isArray(callOptions))) {
221309
+ throw new Error("decide() options must be an object.");
221310
+ }
221311
+ decisionCounter++;
221312
+ await semaphore.acquire();
221313
+ try {
221314
+ if (signal.aborted)
221315
+ throw new Error("Workflow aborted.");
221316
+ return await submitWorkflowDecision({
221317
+ ...callOptions,
221318
+ state,
221319
+ questions
221320
+ }, signal, (result3) => {
221321
+ totalTokens += result3.totalTokens;
221322
+ emit({ kind: "decision_usage", totalTokens: result3.totalTokens });
221323
+ if (options.journalPath) {
221324
+ appendJournalEntry(options.journalPath, {
221325
+ kind: "decision",
221326
+ ...result3
221327
+ });
221328
+ }
221329
+ });
221330
+ } finally {
221331
+ semaphore.release();
221332
+ }
221333
+ }
221064
221334
  async function callAgent(prompt, callOptions) {
221065
221335
  if (typeof prompt !== "string" || !prompt.trim()) {
221066
221336
  throw new Error("agent() requires a non-empty prompt string.");
@@ -221074,6 +221344,9 @@ async function executeWorkflow(spawner, options) {
221074
221344
  throw new Error("agent() options must be an object.");
221075
221345
  }
221076
221346
  const opts = { ...callOptions };
221347
+ if (opts.conversationId !== undefined && (typeof opts.conversationId !== "string" || !/^conv-[A-Za-z0-9_-]+$/.test(opts.conversationId))) {
221348
+ throw new Error("agent() conversationId must be a conv-... ID.");
221349
+ }
221077
221350
  if (opts.maxToolCalls !== undefined && (!Number.isSafeInteger(opts.maxToolCalls) || opts.maxToolCalls <= 0)) {
221078
221351
  throw new Error("agent() maxToolCalls must be a positive safe integer.");
221079
221352
  }
@@ -221088,6 +221361,15 @@ async function executeWorkflow(spawner, options) {
221088
221361
  emit({ kind: "agent", callIndex, label, phase: phase2, status: "running" });
221089
221362
  agentsSpawned++;
221090
221363
  const outcome = await spawner({ prompt, options: opts, callIndex }, signal, {
221364
+ onStarted: (conversationId) => {
221365
+ if (options.journalPath) {
221366
+ appendJournalEntry(options.journalPath, {
221367
+ kind: "agent_started",
221368
+ callIndex,
221369
+ conversationId
221370
+ });
221371
+ }
221372
+ },
221091
221373
  onUsage: (totalTokens2) => emit({
221092
221374
  kind: "agent",
221093
221375
  callIndex,
@@ -221103,6 +221385,7 @@ async function executeWorkflow(spawner, options) {
221103
221385
  callIndex,
221104
221386
  label,
221105
221387
  prompt,
221388
+ ...opts.conversationId ? { resumedConversationId: opts.conversationId } : {},
221106
221389
  outcome
221107
221390
  });
221108
221391
  }
@@ -221172,6 +221455,7 @@ async function executeWorkflow(spawner, options) {
221172
221455
  }
221173
221456
  const context3 = vm2.createContext({
221174
221457
  agent,
221458
+ decide,
221175
221459
  parallel,
221176
221460
  pipeline: pipeline6,
221177
221461
  phase,
@@ -221219,8 +221503,9 @@ ${stripMetaExport(options.script)}
221219
221503
  settled = true;
221220
221504
  }
221221
221505
  }
221222
- var DEFAULT_MAX_CONCURRENT = 16, DEFAULT_MAX_TOTAL_AGENTS = 1000, MAX_ITEMS_PER_HELPER = 4096;
221506
+ var DEFAULT_MAX_CONCURRENT = 16, DEFAULT_MAX_TOTAL_AGENTS = 1000, DEFAULT_MAX_TOTAL_DECISIONS = 1000, MAX_ITEMS_PER_HELPER = 4096;
221223
221507
  var init_workflow_engine = __esm(() => {
221508
+ init_decide();
221224
221509
  init_journal();
221225
221510
  init_meta3();
221226
221511
  });
@@ -221274,7 +221559,8 @@ async function createSdkSpawnerHandle(args, setStage = () => {}) {
221274
221559
  model,
221275
221560
  resolveModel,
221276
221561
  allowedTools: args.allowedTools ?? [...DEFAULT_ALLOWED_TOOLS],
221277
- cwd: getCurrentWorkingDirectory()
221562
+ cwd: getCurrentWorkingDirectory(),
221563
+ supportsAgentFreeResume: sdk.supportsAgentFreeResume
221278
221564
  });
221279
221565
  } catch (error4) {
221280
221566
  client[Symbol.asyncDispose]?.().catch(() => {
@@ -221297,6 +221583,8 @@ function formatWorkflowProgressLine(event) {
221297
221583
  return `── ${event.title} ──`;
221298
221584
  case "log":
221299
221585
  return `» ${event.message}`;
221586
+ case "decision_usage":
221587
+ return null;
221300
221588
  case "agent": {
221301
221589
  if (event.status === "queued")
221302
221590
  return null;
@@ -221342,7 +221630,7 @@ function formatCompletionResult(run, executionDir) {
221342
221630
  return [
221343
221631
  payload,
221344
221632
  "",
221345
- `Per-agent results: ${join65(executionDir, "journal.jsonl")} — one line per completed agent with its full return value.`,
221633
+ `Per-call results: ${join65(executionDir, "journal.jsonl")} — one line per completed agent call with its full return value, plus one per decide() API attempt (retries included) with its model, cost, calibration, and tokens.`,
221346
221634
  "If the result above is empty or unexpected, read that file BEFORE diagnosing — do not assume agents returned non-empty results."
221347
221635
  ].join(`
221348
221636
  `);
@@ -221561,7 +221849,7 @@ ${result3}
221561
221849
  `Workflow launched in background. Task ID: ${taskId}`,
221562
221850
  `Summary: ${meta.description}`,
221563
221851
  `Script file: ${scriptPath}`,
221564
- `Journal: ${journalPath} (one line per completed agent)`,
221852
+ `Journal: ${journalPath} (one line per completed agent, and per decide() API attempt including retries)`,
221565
221853
  `Output file: ${outputFile}`,
221566
221854
  "",
221567
221855
  "You will be notified when it completes. Do not poll or sleep — keep working or end your turn. Read the output file only when you need interim progress; TaskStop aborts the run; the user can watch live status with /workflows."
@@ -398828,7 +399116,7 @@ function createModChangeBatcher(onChange) {
398828
399116
  // src/mods/package-manifest.ts
398829
399117
  import { readFileSync as readFileSync37 } from "node:fs";
398830
399118
  import path35 from "node:path";
398831
- function isRecord9(value) {
399119
+ function isRecord10(value) {
398832
399120
  return typeof value === "object" && value !== null && !Array.isArray(value);
398833
399121
  }
398834
399122
  function addError(errors4, errorPath, message) {
@@ -398967,7 +399255,7 @@ function validateCapabilities(value, errors4) {
398967
399255
  function validateEngines(value, errors4) {
398968
399256
  if (value === undefined)
398969
399257
  return;
398970
- if (!isRecord9(value)) {
399258
+ if (!isRecord10(value)) {
398971
399259
  addError(errors4, "letta.engines", "engines must be an object");
398972
399260
  return;
398973
399261
  }
@@ -398992,7 +399280,7 @@ function validateEngines(value, errors4) {
398992
399280
  return Object.keys(engines).length > 0 ? engines : undefined;
398993
399281
  }
398994
399282
  function parseLettaPackageManifest(packageJson) {
398995
- if (!isRecord9(packageJson)) {
399283
+ if (!isRecord10(packageJson)) {
398996
399284
  return {
398997
399285
  errors: [{ message: "package.json must be an object", path: "package" }],
398998
399286
  manifest: null,
@@ -399003,7 +399291,7 @@ function parseLettaPackageManifest(packageJson) {
399003
399291
  if (rawManifest === undefined) {
399004
399292
  return { errors: [], manifest: null, ok: true };
399005
399293
  }
399006
- if (!isRecord9(rawManifest)) {
399294
+ if (!isRecord10(rawManifest)) {
399007
399295
  return {
399008
399296
  errors: [{ message: "letta manifest must be an object", path: "letta" }],
399009
399297
  manifest: null,
@@ -399073,7 +399361,7 @@ import {
399073
399361
  writeFileSync as writeFileSync28
399074
399362
  } from "node:fs";
399075
399363
  import path36 from "node:path";
399076
- function isRecord10(value) {
399364
+ function isRecord11(value) {
399077
399365
  return typeof value === "object" && value !== null && !Array.isArray(value);
399078
399366
  }
399079
399367
  function createDiagnostic(diagnosticPath, message) {
@@ -399150,7 +399438,7 @@ function readRegistryObject(modsRoot) {
399150
399438
  registryPath
399151
399439
  };
399152
399440
  }
399153
- if (!isRecord10(registryResult.value)) {
399441
+ if (!isRecord11(registryResult.value)) {
399154
399442
  return {
399155
399443
  diagnostics: [
399156
399444
  createDiagnostic(registryPath, "packages.json must be an object")
@@ -399227,7 +399515,7 @@ function resolvePackageEntry(packageRoot, entry) {
399227
399515
  }
399228
399516
  function validatePackageMetadata(modsRoot, rawEntry, index) {
399229
399517
  const diagnosticPath = `packages[${index}]`;
399230
- if (!isRecord10(rawEntry)) {
399518
+ if (!isRecord11(rawEntry)) {
399231
399519
  return {
399232
399520
  diagnostics: [
399233
399521
  createDiagnostic(diagnosticPath, "package registry entry must be an object")
@@ -399594,7 +399882,7 @@ function setManagedModPackageEnabled(params) {
399594
399882
  const registry3 = readMutablePackageRegistry(params.modsRoot);
399595
399883
  const match3 = findPackageIndex(params.modsRoot, registry3.packagesValue, params.specifier);
399596
399884
  const rawEntry = registry3.packagesValue[match3.index];
399597
- if (!isRecord10(rawEntry)) {
399885
+ if (!isRecord11(rawEntry)) {
399598
399886
  throw new Error("Invalid package entry");
399599
399887
  }
399600
399888
  rawEntry.enabled = params.enabled;
@@ -399640,7 +399928,7 @@ function upsertManagedModPackage(params) {
399640
399928
  let removedDuplicates = 0;
399641
399929
  let replaced = false;
399642
399930
  registry3.packagesValue.forEach((rawEntry) => {
399643
- if (isRecord10(rawEntry) && rawEntry.source === params.source) {
399931
+ if (isRecord11(rawEntry) && rawEntry.source === params.source) {
399644
399932
  if (!replaced) {
399645
399933
  insertionIndex = nextPackages.length;
399646
399934
  nextPackages.push(entry);
@@ -399804,7 +400092,7 @@ var init_runtime_dependencies = __esm(() => {
399804
400092
  });
399805
400093
 
399806
400094
  // src/mods/turn-start-cancel.ts
399807
- function isRecord11(value) {
400095
+ function isRecord12(value) {
399808
400096
  return typeof value === "object" && value !== null;
399809
400097
  }
399810
400098
  function normalizeTurnStartCancelReason(value) {
@@ -399816,7 +400104,7 @@ function normalizeTurnStartCancelReason(value) {
399816
400104
  return trimmed.length > MAX_TURN_START_CANCEL_REASON_LENGTH ? trimmed.slice(0, MAX_TURN_START_CANCEL_REASON_LENGTH) : trimmed;
399817
400105
  }
399818
400106
  function getTurnStartCancel(event) {
399819
- if (!isRecord11(event) || !isRecord11(event.cancel))
400107
+ if (!isRecord12(event) || !isRecord12(event.cancel))
399820
400108
  return null;
399821
400109
  const reason = normalizeTurnStartCancelReason(event.cancel.reason);
399822
400110
  return reason ? { reason } : null;
@@ -406461,14 +406749,14 @@ var init_approval_suggestions = __esm(async () => {
406461
406749
  });
406462
406750
 
406463
406751
  // src/websocket/listener/cloud-retry-message.ts
406464
- function isRecord12(value) {
406752
+ function isRecord13(value) {
406465
406753
  return typeof value === "object" && value !== null && !Array.isArray(value);
406466
406754
  }
406467
406755
  function optionalString4(value) {
406468
406756
  return typeof value === "string" && value.length > 0 ? value : null;
406469
406757
  }
406470
406758
  function parseCloudRetryMessage(value) {
406471
- if (!isRecord12(value) || value.message_type !== "retry_message") {
406759
+ if (!isRecord13(value) || value.message_type !== "retry_message") {
406472
406760
  return null;
406473
406761
  }
406474
406762
  const attempt2 = value.attempt;
@@ -421465,7 +421753,7 @@ var init_inbound_dispatch = __esm(async () => {
421465
421753
  });
421466
421754
 
421467
421755
  // src/websocket/listener/protocol-logging.ts
421468
- function isRecord13(value) {
421756
+ function isRecord14(value) {
421469
421757
  return typeof value === "object" && value !== null && !Array.isArray(value);
421470
421758
  }
421471
421759
  function formatLogValue(value) {
@@ -421489,14 +421777,14 @@ function pushField(fields, key2, value, label = key2) {
421489
421777
  fields.push(`${label}=${formatted}`);
421490
421778
  }
421491
421779
  function summarizeInputPayload(payload) {
421492
- if (!isRecord13(payload))
421780
+ if (!isRecord14(payload))
421493
421781
  return [];
421494
421782
  const fields = [];
421495
421783
  pushField(fields, "kind", payload.kind);
421496
421784
  if (payload.kind === "create_message") {
421497
421785
  pushField(fields, "messages", payload.messages);
421498
421786
  pushField(fields, "client_tool_allowlist", payload.client_tool_allowlist);
421499
- if (isRecord13(payload.client_toolset)) {
421787
+ if (isRecord14(payload.client_toolset)) {
421500
421788
  pushField(fields, "client_toolset.base", payload.client_toolset.base);
421501
421789
  pushField(fields, "client_toolset.include", payload.client_toolset.include);
421502
421790
  }
@@ -421513,9 +421801,9 @@ function summarizeRuntimeStartCommand(command) {
421513
421801
  const fields = [];
421514
421802
  pushField(fields, "agent_id", command.agent_id, "agent");
421515
421803
  pushField(fields, "conversation_id", command.conversation_id, "conversation");
421516
- if (isRecord13(command.create_agent))
421804
+ if (isRecord14(command.create_agent))
421517
421805
  fields.push("create_agent=true");
421518
- if (isRecord13(command.create_conversation)) {
421806
+ if (isRecord14(command.create_conversation)) {
421519
421807
  fields.push("create_conversation=true");
421520
421808
  }
421521
421809
  pushField(fields, "cwd", command.cwd);
@@ -421524,17 +421812,17 @@ function summarizeRuntimeStartCommand(command) {
421524
421812
  return fields;
421525
421813
  }
421526
421814
  function summarizeV2Command(parsed) {
421527
- if (!isRecord13(parsed) || typeof parsed.type !== "string")
421815
+ if (!isRecord14(parsed) || typeof parsed.type !== "string")
421528
421816
  return "unknown";
421529
421817
  const fields = [];
421530
- const runtime = isRecord13(parsed.runtime) ? parsed.runtime : null;
421818
+ const runtime = isRecord14(parsed.runtime) ? parsed.runtime : null;
421531
421819
  if (runtime) {
421532
421820
  fields.push(`runtime=${runtime.agent_id ?? "<unknown>"}/${runtime.conversation_id ?? "<unknown>"}`);
421533
421821
  }
421534
421822
  pushField(fields, "request_id", parsed.request_id);
421535
421823
  if (parsed.type === "input") {
421536
421824
  fields.push(...summarizeInputPayload(parsed.payload));
421537
- } else if (parsed.type === "change_device_state" && isRecord13(parsed.payload)) {
421825
+ } else if (parsed.type === "change_device_state" && isRecord14(parsed.payload)) {
421538
421826
  pushField(fields, "mode", parsed.payload.mode);
421539
421827
  pushField(fields, "cwd", parsed.payload.cwd);
421540
421828
  pushField(fields, "agent_id", parsed.payload.agent_id);
@@ -421544,7 +421832,7 @@ function summarizeV2Command(parsed) {
421544
421832
  } else if (parsed.type === "runtime_external_tools_update" && Array.isArray(parsed.updates)) {
421545
421833
  fields.push(`updates=${parsed.updates.length}`);
421546
421834
  fields.push(`runtimes=${parsed.updates.reduce((count, update3) => {
421547
- return count + (isRecord13(update3) && Array.isArray(update3.runtimes) ? update3.runtimes.length : 0);
421835
+ return count + (isRecord14(update3) && Array.isArray(update3.runtimes) ? update3.runtimes.length : 0);
421548
421836
  }, 0)}`);
421549
421837
  } else {
421550
421838
  for (const key2 of [
@@ -424403,7 +424691,7 @@ function decodeJwtClaims(token2, sharedSecret) {
424403
424691
  } catch {
424404
424692
  return { error: unauthorized("invalid websocket jwt") };
424405
424693
  }
424406
- if (!isRecord14(header) || header.alg !== "HS256") {
424694
+ if (!isRecord15(header) || header.alg !== "HS256") {
424407
424695
  return { error: unauthorized("invalid websocket jwt") };
424408
424696
  }
424409
424697
  const expectedSignature = createHmac("sha256", sharedSecret).update(`${encodedHeader}.${encodedClaims}`).digest();
@@ -424441,7 +424729,7 @@ function audienceMatches(actual, expectedAudience) {
424441
424729
  return false;
424442
424730
  }
424443
424731
  function isJwtClaims(value) {
424444
- if (!isRecord14(value) || !Number.isSafeInteger(value.exp)) {
424732
+ if (!isRecord15(value) || !Number.isSafeInteger(value.exp)) {
424445
424733
  return false;
424446
424734
  }
424447
424735
  if (value.nbf !== undefined && !Number.isSafeInteger(value.nbf)) {
@@ -424461,7 +424749,7 @@ function isJwtClaims(value) {
424461
424749
  }
424462
424750
  return true;
424463
424751
  }
424464
- function isRecord14(value) {
424752
+ function isRecord15(value) {
424465
424753
  return typeof value === "object" && value !== null && !Array.isArray(value);
424466
424754
  }
424467
424755
  function base64UrlDecode(value) {
@@ -444556,8 +444844,8 @@ async function connectMcpServer(config2, options = {}) {
444556
444844
  return {
444557
444845
  content: Array.isArray(result3.content) ? result3.content : [],
444558
444846
  ...typeof result3.isError === "boolean" ? { isError: result3.isError } : {},
444559
- ...isRecord15(result3.structuredContent) ? { structuredContent: result3.structuredContent } : {},
444560
- ...isRecord15(result3._meta) ? { _meta: result3._meta } : {}
444847
+ ...isRecord16(result3.structuredContent) ? { structuredContent: result3.structuredContent } : {},
444848
+ ...isRecord16(result3._meta) ? { _meta: result3._meta } : {}
444561
444849
  };
444562
444850
  },
444563
444851
  close: async () => {
@@ -444630,11 +444918,11 @@ function mergeHeaders4(init, headers) {
444630
444918
  };
444631
444919
  }
444632
444920
  function normalizeInputSchema(value) {
444633
- if (isRecord15(value) && value.type === "object")
444921
+ if (isRecord16(value) && value.type === "object")
444634
444922
  return value;
444635
444923
  return { type: "object", properties: {} };
444636
444924
  }
444637
- function isRecord15(value) {
444925
+ function isRecord16(value) {
444638
444926
  return typeof value === "object" && value !== null && !Array.isArray(value);
444639
444927
  }
444640
444928
  var DEFAULT_CLIENT_INFO;
@@ -444646,7 +444934,7 @@ var init_mcp_client = __esm(() => {
444646
444934
  init_streamableHttp();
444647
444935
  DEFAULT_CLIENT_INFO = {
444648
444936
  name: "letta-code",
444649
- version: "0.33.4"
444937
+ version: "0.33.5"
444650
444938
  };
444651
444939
  });
444652
444940
 
@@ -447314,7 +447602,7 @@ import {
447314
447602
  } from "node:fs";
447315
447603
  import { tmpdir as tmpdir12 } from "node:os";
447316
447604
  import path45 from "node:path";
447317
- function isRecord16(value) {
447605
+ function isRecord17(value) {
447318
447606
  return typeof value === "object" && value !== null && !Array.isArray(value);
447319
447607
  }
447320
447608
  function isPathInsideOrEqual2(childPath, parentPath) {
@@ -447330,7 +447618,7 @@ function readPackageJson(packageJsonPath) {
447330
447618
  } catch (error5) {
447331
447619
  throw new Error(`Could not read package.json: ${error5 instanceof Error ? error5.message : String(error5)}`);
447332
447620
  }
447333
- if (!isRecord16(parsed)) {
447621
+ if (!isRecord17(parsed)) {
447334
447622
  throw new Error("package.json must be an object");
447335
447623
  }
447336
447624
  return parsed;
@@ -447340,7 +447628,7 @@ function formatRepository(repository) {
447340
447628
  const trimmed2 = repository.trim();
447341
447629
  return trimmed2 || undefined;
447342
447630
  }
447343
- if (!isRecord16(repository))
447631
+ if (!isRecord17(repository))
447344
447632
  return;
447345
447633
  const url2 = repository.url;
447346
447634
  if (typeof url2 !== "string")
@@ -447870,7 +448158,7 @@ function hasRuntimeDependencies(packageJson) {
447870
448158
  if (!packageJson)
447871
448159
  return false;
447872
448160
  const dependencies4 = packageJson.dependencies;
447873
- return isRecord16(dependencies4) && Object.keys(dependencies4).length > 0;
448161
+ return isRecord17(dependencies4) && Object.keys(dependencies4).length > 0;
447874
448162
  }
447875
448163
  function readPackageJsonIfExists(packageDirectory) {
447876
448164
  const packageJsonPath = path45.join(packageDirectory, "package.json");
@@ -511968,4 +512256,4 @@ function registerBunOAuthFlows() {
511968
512256
  registerBunOAuthFlows();
511969
512257
  await init_src5().then(() => exports_src2);
511970
512258
 
511971
- //# debugId=E297E0391CF07BFB64756E2164756E21
512259
+ //# debugId=4BF692CA3D7D262964756E2164756E21