@letta-ai/letta-agent-sdk 0.6.3 → 0.7.1

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/index.js CHANGED
@@ -3091,7 +3091,7 @@ function isAppServerInfoResponseMessage(message) {
3091
3091
  return false;
3092
3092
  }
3093
3093
  const capabilityRecord = capabilities;
3094
- return candidate.type === "app_server_info_response" && typeof candidate.request_id === "string" && candidate.request_id.length > 0 && candidate.success === true && (candidate.backend === "local" || candidate.backend === "api") && typeof candidate.letta_code_version === "string" && typeof candidate.protocol_version === "number" && Number.isInteger(candidate.protocol_version) && typeof capabilityRecord.agent_management === "boolean" && typeof capabilityRecord.conversation_management === "boolean" && typeof capabilityRecord.memory_management === "boolean" && typeof capabilityRecord.runtime_start === "boolean" && typeof capabilityRecord.split_channels === "boolean";
3094
+ return candidate.type === "app_server_info_response" && typeof candidate.request_id === "string" && candidate.request_id.length > 0 && candidate.success === true && (candidate.backend === "local" || candidate.backend === "api") && typeof candidate.letta_code_version === "string" && typeof candidate.protocol_version === "number" && Number.isInteger(candidate.protocol_version) && typeof capabilityRecord.agent_management === "boolean" && typeof capabilityRecord.conversation_management === "boolean" && typeof capabilityRecord.memory_management === "boolean" && typeof capabilityRecord.runtime_start === "boolean" && (capabilityRecord.runtime_external_tools_update === undefined || typeof capabilityRecord.runtime_external_tools_update === "boolean") && typeof capabilityRecord.split_channels === "boolean";
3095
3095
  }
3096
3096
  var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
3097
3097
  var WEBSOCKET_OPEN_STATE = 1;
@@ -3334,6 +3334,16 @@ class AppServerClient {
3334
3334
  predicate: (message) => message.type === "runtime_start_response"
3335
3335
  });
3336
3336
  }
3337
+ runtimeExternalToolsUpdate(command, options = {}) {
3338
+ return this.request({
3339
+ type: "runtime_external_tools_update",
3340
+ request_id: command.request_id ?? this.nextRequestId("runtime-external-tools"),
3341
+ ...command
3342
+ }, {
3343
+ ...options,
3344
+ predicate: (message) => message.type === "runtime_external_tools_update_response"
3345
+ });
3346
+ }
3337
3347
  sync(command, options = {}) {
3338
3348
  return this.request({
3339
3349
  type: "sync",
@@ -3854,12 +3864,7 @@ You **MUST** be proactive in creating crons when work extends beyond the current
3854
3864
 
3855
3865
  **Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
3856
3866
 
3857
- Creating crons:
3858
- - One-shot follow-up: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --at "in 30m"\`
3859
- - Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
3860
- Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
3861
-
3862
- Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
3867
+ The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
3863
3868
 
3864
3869
  # Harness Architecture
3865
3870
 
@@ -4036,12 +4041,7 @@ You **MUST** be proactive in creating crons when work extends beyond the current
4036
4041
 
4037
4042
  **Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
4038
4043
 
4039
- Creating crons:
4040
- - One-shot follow-up: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --at "in 30m"\`
4041
- - Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
4042
- Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
4043
-
4044
- Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
4044
+ The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
4045
4045
 
4046
4046
  # Harness Architecture
4047
4047
 
@@ -9071,6 +9071,20 @@ function toSessionDeviceStatus(status) {
9071
9071
  function normalizeSendMessage(message) {
9072
9072
  return message;
9073
9073
  }
9074
+ function normalizeCallerOtid(otid) {
9075
+ if (otid === undefined)
9076
+ return;
9077
+ if (typeof otid !== "string" || otid.trim() === "") {
9078
+ throw new Error("send() otid must be a non-empty string");
9079
+ }
9080
+ return otid;
9081
+ }
9082
+ function turnSendOptions(turn) {
9083
+ return {
9084
+ clientMessageId: turn.clientMessageId,
9085
+ ...turn.otid !== undefined ? { otid: turn.otid } : {}
9086
+ };
9087
+ }
9074
9088
 
9075
9089
  // src/remote-turn-coordinator.ts
9076
9090
  class RemoteTurnCoordinator {
@@ -9099,11 +9113,13 @@ class RemoteTurnCoordinator {
9099
9113
  hasInFlightTurn() {
9100
9114
  return this.activeTurn !== null || this.pendingTurns.length > 0;
9101
9115
  }
9102
- trackSentTurn(runtime) {
9116
+ trackSentTurn(runtime, callerOtid) {
9117
+ const otid = normalizeCallerOtid(callerOtid);
9103
9118
  const turn = {
9104
9119
  id: ++this.nextTurnId,
9105
9120
  runtime,
9106
- clientMessageId: `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
9121
+ ...otid !== undefined ? { otid } : {},
9122
+ clientMessageId: otid ?? `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
9107
9123
  queuedAt: Date.now(),
9108
9124
  startedAt: 0,
9109
9125
  assistantText: "",
@@ -9605,7 +9621,7 @@ class RemoteClientSessionCore {
9605
9621
  this.toolNames = undefined;
9606
9622
  this.initialized = false;
9607
9623
  }
9608
- async send(message) {
9624
+ async send(message, options) {
9609
9625
  if (this.closed)
9610
9626
  throw new Error("Session is closed");
9611
9627
  if (!this.initialized) {
@@ -9621,11 +9637,9 @@ class RemoteClientSessionCore {
9621
9637
  if (!controller || !runtime) {
9622
9638
  throw new Error("Session transport disconnected before the turn was sent");
9623
9639
  }
9624
- const turn = this.turns.trackSentTurn(runtime);
9640
+ const turn = this.turns.trackSentTurn(runtime, options?.otid);
9625
9641
  try {
9626
- controller.sendTurnMessage(runtime, message, {
9627
- clientMessageId: turn.clientMessageId
9628
- });
9642
+ controller.sendTurnMessage(runtime, message, turnSendOptions(turn));
9629
9643
  } catch (error) {
9630
9644
  this.turns.removeTrackedTurn(turn);
9631
9645
  throw error;
@@ -9704,6 +9718,9 @@ class RemoteClientSessionCore {
9704
9718
  return this.controller.listModels();
9705
9719
  }
9706
9720
  async updateModel(update) {
9721
+ if (this.mode.kind === "session" && this.mode.options.stateless === true) {
9722
+ throw new Error("updateModel() is unavailable in a stateless session because it changes persisted agent configuration.");
9723
+ }
9707
9724
  if (!this.initialized) {
9708
9725
  await this.initialize();
9709
9726
  }
@@ -10104,7 +10121,8 @@ class RemoteClientSessionCore {
10104
10121
  ensureSuccess(response, "Failed to enable memfs");
10105
10122
  }
10106
10123
  const dreamingSettings = resolveDreamingSettings(options.dreaming);
10107
- if (dreamingSettings) {
10124
+ const isStatelessSession = this.mode.kind === "session" && this.mode.options.stateless === true;
10125
+ if (dreamingSettings && !isStatelessSession) {
10108
10126
  const response = await this.controller.request("set_reflection_settings", {
10109
10127
  runtime: this.runtime,
10110
10128
  settings: dreamingSettings,
@@ -10370,7 +10388,8 @@ class AppServerRuntimeController {
10370
10388
  {
10371
10389
  role: "user",
10372
10390
  content: normalizeSendMessage(message),
10373
- client_message_id: options.clientMessageId
10391
+ client_message_id: options.clientMessageId,
10392
+ ...options.otid !== undefined ? { otid: options.otid } : {}
10374
10393
  }
10375
10394
  ]
10376
10395
  };
@@ -10597,6 +10616,9 @@ class AppServerSession extends RemoteClientSessionCore {
10597
10616
  command.mode = mode;
10598
10617
  if (options.cwd !== undefined)
10599
10618
  command.cwd = options.cwd;
10619
+ if (this.mode.kind === "session" && this.mode.options.stateless === true) {
10620
+ command.stateless = true;
10621
+ }
10600
10622
  if (options.skillSources !== undefined) {
10601
10623
  command.skill_sources = [...new Set(options.skillSources)];
10602
10624
  }
@@ -11454,6 +11476,8 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11454
11476
  command.mode = mode;
11455
11477
  if (options.cwd !== undefined)
11456
11478
  command.cwd = options.cwd;
11479
+ if (this.cloudMode.options.stateless === true)
11480
+ command.stateless = true;
11457
11481
  if (options.skillSources !== undefined) {
11458
11482
  command.skill_sources = [...new Set(options.skillSources)];
11459
11483
  }
@@ -12058,6 +12082,20 @@ function validateMcpServers(servers) {
12058
12082
  }
12059
12083
  }
12060
12084
  function validateCreateSessionOptions(options) {
12085
+ if (options.stateless !== undefined && typeof options.stateless !== "boolean") {
12086
+ throw new Error("Invalid stateless. Expected a boolean.");
12087
+ }
12088
+ if (options.stateless) {
12089
+ const persistedOption = [
12090
+ ["model", options.model],
12091
+ ["reasoningEffort", options.reasoningEffort],
12092
+ ["dreaming", options.dreaming],
12093
+ ["resources", options.resources]
12094
+ ].find(([, value]) => value !== undefined);
12095
+ if (persistedOption) {
12096
+ throw new Error(`stateless sessions cannot set ${persistedOption[0]} because it changes persisted agent configuration.`);
12097
+ }
12098
+ }
12061
12099
  validateClientToolset(options.toolset);
12062
12100
  validateSkillSources(options.skillSources);
12063
12101
  validateMcpServers(options.mcpServers);
@@ -29636,7 +29674,7 @@ class StreamableHTTPClientTransport {
29636
29674
  }
29637
29675
  var DEFAULT_CLIENT_INFO = {
29638
29676
  name: "letta-code",
29639
- version: "0.30.5"
29677
+ version: "0.30.11"
29640
29678
  };
29641
29679
  async function connectMcpServer(config2, options = {}) {
29642
29680
  let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
@@ -29954,6 +29992,440 @@ function extractStreamTextDelta(event) {
29954
29992
  }
29955
29993
  return null;
29956
29994
  }
29995
+ // src/transcript-accumulator.ts
29996
+ var SEP = ":";
29997
+ var MAX_TRACKED_RUNS = 64;
29998
+ var ANONYMOUS_RUN = "";
29999
+ function familyOtidAlias(kind, otid) {
30000
+ return `${kind}${SEP}otid${SEP}${otid}`;
30001
+ }
30002
+ function familyUuidAlias(kind, uuid2) {
30003
+ return `${kind}${SEP}uuid${SEP}${uuid2}`;
30004
+ }
30005
+ function toolRowKey(toolCallId) {
30006
+ return `tool_call${SEP}id${SEP}${toolCallId}`;
30007
+ }
30008
+ function asRecord(value) {
30009
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
30010
+ }
30011
+ function readString(record2, field) {
30012
+ const value = record2[field];
30013
+ return typeof value === "string" && value.length > 0 ? value : undefined;
30014
+ }
30015
+ function readNumber(record2, field) {
30016
+ const value = record2[field];
30017
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
30018
+ }
30019
+ function parseJsonObject(raw) {
30020
+ const trimmed = raw.trim();
30021
+ if (trimmed.length === 0)
30022
+ return;
30023
+ try {
30024
+ return asRecord(JSON.parse(trimmed));
30025
+ } catch {
30026
+ return;
30027
+ }
30028
+ }
30029
+ function isRawArgumentsWrapper(input, rawArguments) {
30030
+ if (!input)
30031
+ return false;
30032
+ const keys = Object.keys(input);
30033
+ if (keys.length !== 1 || keys[0] !== "raw")
30034
+ return false;
30035
+ const wrapped = input.raw;
30036
+ if (typeof wrapped !== "string")
30037
+ return false;
30038
+ return rawArguments === undefined || wrapped === rawArguments;
30039
+ }
30040
+ function toolStatus(row) {
30041
+ if (row.result)
30042
+ return "complete";
30043
+ return row.argumentsComplete ? "ready" : "streaming";
30044
+ }
30045
+ function mergeToolArguments(previous, merge2) {
30046
+ let { rawArguments, toolInput, argumentsComplete } = previous;
30047
+ const fragment = merge2.rawArguments;
30048
+ const wrapped = isRawArgumentsWrapper(merge2.toolInput, fragment);
30049
+ if (fragment !== undefined && fragment.length > 0) {
30050
+ const whole = parseJsonObject(fragment);
30051
+ if (whole) {
30052
+ return { rawArguments: fragment, toolInput: whole, argumentsComplete: true };
30053
+ }
30054
+ if (argumentsComplete) {
30055
+ return previous;
30056
+ }
30057
+ if (merge2.mode === "whole") {
30058
+ return { rawArguments: rawArguments ?? fragment, toolInput, argumentsComplete };
30059
+ }
30060
+ rawArguments = (rawArguments ?? "") + fragment;
30061
+ const parsed = parseJsonObject(rawArguments);
30062
+ if (parsed) {
30063
+ return { rawArguments, toolInput: parsed, argumentsComplete: true };
30064
+ }
30065
+ return { rawArguments, toolInput, argumentsComplete: false };
30066
+ }
30067
+ if (!wrapped && merge2.toolInput) {
30068
+ const keys = Object.keys(merge2.toolInput);
30069
+ if (keys.length > 0) {
30070
+ return { rawArguments, toolInput: merge2.toolInput, argumentsComplete: true };
30071
+ }
30072
+ if (rawArguments === undefined) {
30073
+ return { rawArguments, toolInput, argumentsComplete: true };
30074
+ }
30075
+ }
30076
+ return previous;
30077
+ }
30078
+
30079
+ class TranscriptAccumulatorImpl {
30080
+ byKey = new Map;
30081
+ aliasByOtid = new Map;
30082
+ aliasByUuid = new Map;
30083
+ seqThresholds = new Map;
30084
+ snapshot = null;
30085
+ anonymousCounter = 0;
30086
+ apply(message) {
30087
+ switch (message.type) {
30088
+ case "assistant":
30089
+ case "reasoning":
30090
+ this.applyText({
30091
+ kind: message.type,
30092
+ text: message.content,
30093
+ uuid: message.uuid,
30094
+ otid: typeof message.otid === "string" ? message.otid : undefined,
30095
+ runId: message.runId,
30096
+ seqId: message.seqId
30097
+ });
30098
+ break;
30099
+ case "tool_call":
30100
+ this.mergeToolCall({
30101
+ toolCallId: message.toolCallId,
30102
+ toolName: message.toolName,
30103
+ toolInput: message.toolInput,
30104
+ rawArguments: message.rawArguments,
30105
+ uuid: message.uuid,
30106
+ runId: message.runId,
30107
+ mode: "fragment"
30108
+ });
30109
+ break;
30110
+ case "tool_result":
30111
+ this.mergeToolResult({
30112
+ toolCallId: message.toolCallId,
30113
+ content: message.content,
30114
+ isError: message.isError,
30115
+ uuid: message.uuid,
30116
+ runId: message.runId
30117
+ });
30118
+ break;
30119
+ case "stream_event":
30120
+ this.applyStreamEvent(message);
30121
+ break;
30122
+ default:
30123
+ break;
30124
+ }
30125
+ return this.rows();
30126
+ }
30127
+ rebase(page, options) {
30128
+ const messages = normalizeHistoryPage(page, options?.order);
30129
+ if (messages.length === 0)
30130
+ return this.rows();
30131
+ const historyKeys = [];
30132
+ const seen = new Set;
30133
+ for (const message of messages) {
30134
+ const key = this.applyHistoryMessage(message);
30135
+ if (!key || seen.has(key))
30136
+ continue;
30137
+ seen.add(key);
30138
+ historyKeys.push(key);
30139
+ }
30140
+ this.reorder(historyKeys);
30141
+ return this.rows();
30142
+ }
30143
+ rows() {
30144
+ if (!this.snapshot) {
30145
+ this.snapshot = Array.from(this.byKey.values());
30146
+ }
30147
+ return this.snapshot;
30148
+ }
30149
+ reset() {
30150
+ this.byKey.clear();
30151
+ this.aliasByOtid.clear();
30152
+ this.aliasByUuid.clear();
30153
+ this.seqThresholds.clear();
30154
+ this.anonymousCounter = 0;
30155
+ this.snapshot = null;
30156
+ }
30157
+ isReplay(runId, seqId) {
30158
+ if (seqId === undefined)
30159
+ return false;
30160
+ const bucket = runId ?? ANONYMOUS_RUN;
30161
+ const threshold = this.seqThresholds.get(bucket);
30162
+ if (threshold !== undefined && seqId <= threshold)
30163
+ return true;
30164
+ this.rememberSeq(bucket, seqId);
30165
+ return false;
30166
+ }
30167
+ rememberSeq(bucket, seqId) {
30168
+ const threshold = this.seqThresholds.get(bucket);
30169
+ if (threshold !== undefined && threshold >= seqId)
30170
+ return;
30171
+ this.seqThresholds.set(bucket, seqId);
30172
+ while (this.seqThresholds.size > MAX_TRACKED_RUNS) {
30173
+ const oldest = this.seqThresholds.keys().next();
30174
+ if (oldest.done)
30175
+ break;
30176
+ this.seqThresholds.delete(oldest.value);
30177
+ }
30178
+ }
30179
+ applyText(slice) {
30180
+ if (this.isReplay(slice.runId, slice.seqId))
30181
+ return;
30182
+ this.writeText(slice, "append");
30183
+ }
30184
+ writeText(slice, write) {
30185
+ const key = this.resolveTextKey(slice.kind, slice.uuid, slice.otid);
30186
+ const existing = this.byKey.get(key);
30187
+ const previous = existing && existing.kind === slice.kind ? existing : undefined;
30188
+ this.setRow(key, {
30189
+ kind: slice.kind,
30190
+ key,
30191
+ text: write === "append" ? (previous?.text ?? "") + slice.text : slice.text,
30192
+ uuid: previous?.uuid ?? slice.uuid,
30193
+ otid: slice.otid ?? previous?.otid,
30194
+ runId: slice.runId ?? previous?.runId,
30195
+ seqId: maxDefined(slice.seqId, previous?.seqId)
30196
+ });
30197
+ return key;
30198
+ }
30199
+ resolveTextKey(kind, uuid2, otid) {
30200
+ const otidAlias = otid ? familyOtidAlias(kind, otid) : undefined;
30201
+ const uuidAlias = uuid2 ? familyUuidAlias(kind, uuid2) : undefined;
30202
+ const fromOtid = otidAlias ? this.aliasByOtid.get(otidAlias) : undefined;
30203
+ const fromUuid = uuidAlias ? this.aliasByUuid.get(uuidAlias) : undefined;
30204
+ let key = fromOtid ?? fromUuid;
30205
+ if (key && !fromOtid && otid) {
30206
+ const existing = this.byKey.get(key);
30207
+ if (existing && existing.otid !== undefined && existing.otid !== otid) {
30208
+ key = undefined;
30209
+ }
30210
+ }
30211
+ if (!key) {
30212
+ key = otidAlias ?? uuidAlias ?? `${kind}${SEP}auto${SEP}${++this.anonymousCounter}`;
30213
+ }
30214
+ if (otidAlias)
30215
+ this.aliasByOtid.set(otidAlias, key);
30216
+ if (uuidAlias)
30217
+ this.aliasByUuid.set(uuidAlias, key);
30218
+ return key;
30219
+ }
30220
+ mergeToolCall(merge2) {
30221
+ const key = toolRowKey(merge2.toolCallId);
30222
+ const existing = this.byKey.get(key);
30223
+ const previous = existing && existing.kind === "tool_call" ? existing : undefined;
30224
+ const args = mergeToolArguments({
30225
+ rawArguments: previous?.rawArguments,
30226
+ toolInput: previous?.toolInput ?? {},
30227
+ argumentsComplete: previous?.argumentsComplete ?? false
30228
+ }, merge2);
30229
+ const toolName = merge2.toolName && merge2.toolName !== "?" ? merge2.toolName : previous?.toolName ?? merge2.toolName ?? "?";
30230
+ const next = {
30231
+ kind: "tool_call",
30232
+ key,
30233
+ toolCallId: merge2.toolCallId,
30234
+ toolName,
30235
+ toolInput: args.toolInput,
30236
+ ...args.rawArguments !== undefined ? { rawArguments: args.rawArguments } : {},
30237
+ argumentsComplete: args.argumentsComplete,
30238
+ ...previous?.result ? { result: previous.result } : {},
30239
+ status: "streaming",
30240
+ uuid: previous?.uuid ?? merge2.uuid,
30241
+ runId: merge2.runId ?? previous?.runId,
30242
+ seqId: maxDefined(merge2.seqId, previous?.seqId)
30243
+ };
30244
+ next.status = toolStatus(next);
30245
+ this.setRow(key, next);
30246
+ return key;
30247
+ }
30248
+ mergeToolResult(merge2) {
30249
+ const key = toolRowKey(merge2.toolCallId);
30250
+ const existing = this.byKey.get(key);
30251
+ const previous = existing && existing.kind === "tool_call" ? existing : undefined;
30252
+ this.setRow(key, {
30253
+ kind: "tool_call",
30254
+ key,
30255
+ toolCallId: merge2.toolCallId,
30256
+ toolName: previous?.toolName ?? "?",
30257
+ toolInput: previous?.toolInput ?? {},
30258
+ ...previous?.rawArguments !== undefined ? { rawArguments: previous.rawArguments } : {},
30259
+ argumentsComplete: previous?.argumentsComplete ?? false,
30260
+ result: {
30261
+ content: merge2.content,
30262
+ isError: merge2.isError,
30263
+ ...merge2.uuid ? { uuid: merge2.uuid } : {}
30264
+ },
30265
+ status: "complete",
30266
+ uuid: previous?.uuid,
30267
+ runId: merge2.runId ?? previous?.runId,
30268
+ seqId: maxDefined(merge2.seqId, previous?.seqId)
30269
+ });
30270
+ return key;
30271
+ }
30272
+ applyStreamEvent(message) {
30273
+ const delta = extractStreamTextDelta(message.event);
30274
+ if (!delta)
30275
+ return;
30276
+ const payload = asRecord(message.event);
30277
+ const otid = payload ? readString(payload, "otid") : undefined;
30278
+ const payloadId = payload ? readString(payload, "id") : undefined;
30279
+ const identified = otid !== undefined || payloadId !== undefined;
30280
+ this.applyText({
30281
+ kind: delta.kind,
30282
+ text: delta.text,
30283
+ uuid: identified ? payloadId : `${delta.kind}${SEP}live`,
30284
+ otid,
30285
+ runId: payload ? readString(payload, "run_id") : undefined,
30286
+ seqId: payload ? readNumber(payload, "seq_id") : undefined
30287
+ });
30288
+ }
30289
+ applyHistoryMessage(message) {
30290
+ const record2 = asRecord(message);
30291
+ if (!record2)
30292
+ return;
30293
+ const messageType = readString(record2, "message_type");
30294
+ if (!messageType)
30295
+ return;
30296
+ const uuid2 = readString(record2, "id");
30297
+ const otid = readString(record2, "otid");
30298
+ const runId = readString(record2, "run_id");
30299
+ const seqId = readNumber(record2, "seq_id");
30300
+ if (seqId !== undefined) {
30301
+ this.rememberSeq(runId ?? ANONYMOUS_RUN, seqId);
30302
+ }
30303
+ switch (messageType) {
30304
+ case "user_message":
30305
+ case "assistant_message": {
30306
+ const text = extractTextFromContent(record2.content);
30307
+ if (text === null)
30308
+ return;
30309
+ return this.writeText({
30310
+ kind: messageType === "user_message" ? "user" : "assistant",
30311
+ text,
30312
+ uuid: uuid2,
30313
+ otid,
30314
+ runId,
30315
+ seqId
30316
+ }, "replace");
30317
+ }
30318
+ case "reasoning_message": {
30319
+ const text = typeof record2.reasoning === "string" ? record2.reasoning : extractTextFromContent(record2.content);
30320
+ if (text === null || text === undefined)
30321
+ return;
30322
+ return this.writeText({ kind: "reasoning", text, uuid: uuid2, otid, runId, seqId }, "replace");
30323
+ }
30324
+ case "tool_call_message":
30325
+ case "approval_request_message": {
30326
+ const toolCall = firstToolCall(record2);
30327
+ if (!toolCall)
30328
+ return;
30329
+ const fn = asRecord(toolCall.function);
30330
+ const toolCallId = (typeof toolCall.tool_call_id === "string" ? toolCall.tool_call_id : undefined) ?? (typeof toolCall.id === "string" ? toolCall.id : undefined);
30331
+ if (!toolCallId)
30332
+ return;
30333
+ const args = toolCall.arguments ?? fn?.arguments;
30334
+ return this.mergeToolCall({
30335
+ toolCallId,
30336
+ toolName: (typeof toolCall.name === "string" ? toolCall.name : undefined) ?? (typeof fn?.name === "string" ? fn.name : undefined),
30337
+ toolInput: asRecord(args),
30338
+ rawArguments: typeof args === "string" ? args : undefined,
30339
+ uuid: uuid2,
30340
+ runId,
30341
+ seqId,
30342
+ mode: "whole"
30343
+ });
30344
+ }
30345
+ case "tool_return_message": {
30346
+ const toolReturn = firstToolReturn(record2) ?? record2;
30347
+ const toolCallId = readString(record2, "tool_call_id") ?? (typeof toolReturn.tool_call_id === "string" ? toolReturn.tool_call_id : undefined);
30348
+ if (!toolCallId)
30349
+ return;
30350
+ const content = extractTextFromContent(record2.tool_return ?? toolReturn.tool_return ?? toolReturn.content) ?? "";
30351
+ const status = readString(record2, "status") ?? (typeof toolReturn.status === "string" ? toolReturn.status : undefined);
30352
+ return this.mergeToolResult({
30353
+ toolCallId,
30354
+ content,
30355
+ isError: status === "error",
30356
+ uuid: uuid2,
30357
+ runId,
30358
+ seqId
30359
+ });
30360
+ }
30361
+ default:
30362
+ return;
30363
+ }
30364
+ }
30365
+ setRow(key, row) {
30366
+ this.byKey.set(key, row);
30367
+ this.snapshot = null;
30368
+ }
30369
+ reorder(historyKeys) {
30370
+ if (historyKeys.length === 0)
30371
+ return;
30372
+ const historySet = new Set(historyKeys);
30373
+ const reordered = new Map;
30374
+ for (const key of historyKeys) {
30375
+ const row = this.byKey.get(key);
30376
+ if (row)
30377
+ reordered.set(key, row);
30378
+ }
30379
+ for (const [key, row] of this.byKey) {
30380
+ if (historySet.has(key))
30381
+ continue;
30382
+ reordered.set(key, row);
30383
+ }
30384
+ this.byKey = reordered;
30385
+ this.snapshot = null;
30386
+ }
30387
+ }
30388
+ function maxDefined(next, previous) {
30389
+ if (next === undefined)
30390
+ return previous;
30391
+ if (previous === undefined)
30392
+ return next;
30393
+ return Math.max(next, previous);
30394
+ }
30395
+ function normalizeHistoryPage(page, order) {
30396
+ const messages = Array.isArray(page) ? page : page.messages ?? [];
30397
+ if (messages.length < 2)
30398
+ return messages;
30399
+ const descending = order ? order === "desc" : detectDescending(messages);
30400
+ return descending ? [...messages].reverse() : messages;
30401
+ }
30402
+ function detectDescending(messages) {
30403
+ const seqIds = [];
30404
+ const dates = [];
30405
+ for (const message of messages) {
30406
+ const record2 = asRecord(message);
30407
+ if (!record2)
30408
+ continue;
30409
+ const seqId = readNumber(record2, "seq_id");
30410
+ if (seqId !== undefined)
30411
+ seqIds.push(seqId);
30412
+ const date4 = readString(record2, "date");
30413
+ if (!date4)
30414
+ continue;
30415
+ const parsed = Date.parse(date4);
30416
+ if (!Number.isNaN(parsed))
30417
+ dates.push(parsed);
30418
+ }
30419
+ const ordered = seqIds.length >= 2 ? seqIds : dates;
30420
+ if (ordered.length < 2)
30421
+ return false;
30422
+ const first = ordered[0];
30423
+ const last = ordered[ordered.length - 1];
30424
+ return last < first;
30425
+ }
30426
+ function createTranscriptAccumulator() {
30427
+ return new TranscriptAccumulatorImpl;
30428
+ }
29957
30429
  // src/tool-helpers.ts
29958
30430
  function jsonResult(payload) {
29959
30431
  return {
@@ -30126,6 +30598,7 @@ export {
30126
30598
  imageFromFile,
30127
30599
  imageFromBase64,
30128
30600
  extractStreamTextDelta,
30601
+ createTranscriptAccumulator,
30129
30602
  createSession,
30130
30603
  createReactNativeWebSocketConstructor,
30131
30604
  createAgent,
@@ -30134,4 +30607,4 @@ export {
30134
30607
  CloudManagedSandboxExpiredError
30135
30608
  };
30136
30609
 
30137
- //# debugId=C079C2D0B42E0C4F64756E2164756E21
30610
+ //# debugId=45035C35D5C51AFA64756E2164756E21