@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/AGENTS.md +42 -0
- package/README.md +26 -283
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-entry.d.ts +2 -0
- package/dist/client-entry.d.ts.map +1 -1
- package/dist/client-entry.js +496 -23
- package/dist/client-entry.js.map +13 -12
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +497 -24
- package/dist/index.js.map +14 -13
- package/dist/remote-client-session-core.d.ts +2 -2
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +11 -0
- package/dist/remote-session-protocol.d.ts.map +1 -1
- package/dist/remote-turn-coordinator.d.ts +6 -1
- package/dist/remote-turn-coordinator.d.ts.map +1 -1
- package/dist/transcript-accumulator.d.ts +133 -0
- package/dist/transcript-accumulator.d.ts.map +1 -0
- package/dist/types.d.ts +29 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/app-server-session.ts +9 -0
- package/src/client-entry.ts +14 -0
- package/src/cloud-session.ts +1 -0
- package/src/index.ts +15 -0
- package/src/remote-client-session-core.ts +15 -10
- package/src/remote-session-protocol.ts +24 -0
- package/src/remote-turn-coordinator.ts +11 -2
- package/src/transcript-accumulator.ts +823 -0
- package/src/types.ts +31 -1
- package/src/validation.ts +16 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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=
|
|
30610
|
+
//# debugId=45035C35D5C51AFA64756E2164756E21
|