ai-sdk-letta 0.1.0
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/CHANGELOG.md +7 -0
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +35 -0
- package/dist/agent.d.ts +108 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +371 -0
- package/dist/agent.js.map +1 -0
- package/dist/definition.d.ts +81 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +87 -0
- package/dist/definition.js.map +1 -0
- package/dist/history.d.ts +38 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +226 -0
- package/dist/history.js.map +1 -0
- package/dist/identity.d.ts +43 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +158 -0
- package/dist/identity.js.map +1 -0
- package/dist/images.d.ts +83 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +181 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions.d.ts +61 -0
- package/dist/interactions.d.ts.map +1 -0
- package/dist/interactions.js +110 -0
- package/dist/interactions.js.map +1 -0
- package/dist/memory.d.ts +11 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +55 -0
- package/dist/memory.js.map +1 -0
- package/dist/navigation.d.ts +50 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +87 -0
- package/dist/navigation.js.map +1 -0
- package/dist/runtime.d.ts +83 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +246 -0
- package/dist/runtime.js.map +1 -0
- package/dist/state.d.ts +24 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +39 -0
- package/dist/state.js.map +1 -0
- package/dist/tools.d.ts +83 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +162 -0
- package/dist/tools.js.map +1 -0
- package/package.json +67 -0
- package/src/agent.ts +334 -0
- package/src/definition.ts +136 -0
- package/src/history.ts +198 -0
- package/src/identity.ts +125 -0
- package/src/images.ts +184 -0
- package/src/index.ts +23 -0
- package/src/interactions.ts +121 -0
- package/src/memory.ts +42 -0
- package/src/navigation.ts +84 -0
- package/src/runtime.ts +243 -0
- package/src/state.ts +39 -0
- package/src/tools.ts +166 -0
package/dist/history.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { decodeImagePart } from './images.js';
|
|
2
|
+
/** Maximum backend records loaded when restoring a conversation's display history. */
|
|
3
|
+
export const HISTORY_LIMIT = 10_000;
|
|
4
|
+
const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
5
|
+
/** Strip terminal escape sequences and control characters from untrusted text. */
|
|
6
|
+
export const sanitizeText = (text) => text.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '').replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, '');
|
|
7
|
+
/** The installed SDK drops agent_id for default history. Its supported command
|
|
8
|
+
* escape hatch forwards the backend's agent-scoped query, including real cursors.
|
|
9
|
+
* Named conversations use the ordinary SDK API. Neither path sends input.
|
|
10
|
+
*/
|
|
11
|
+
export async function historyPage(session, agentId, conversationId, options, timeoutMs) {
|
|
12
|
+
if (conversationId !== 'default' && timeoutMs === undefined)
|
|
13
|
+
return session.listMessages({ ...options, conversationId });
|
|
14
|
+
const response = await session.sendCommand({ type: 'conversation_messages_list', conversation_id: conversationId, query: { ...options, agent_id: agentId } }, { responseType: 'conversation_messages_list_response', ...(timeoutMs === undefined ? {} : { timeoutMs }) });
|
|
15
|
+
if (response.success !== true || !Array.isArray(response.messages))
|
|
16
|
+
throw new Error('Unable to load authoritative default conversation history');
|
|
17
|
+
return { messages: response.messages,
|
|
18
|
+
...(typeof response.has_more === 'boolean' ? { hasMore: response.has_more } : {}),
|
|
19
|
+
...(typeof response.next_before === 'string' || response.next_before === null ? { nextBefore: response.next_before } : {}) };
|
|
20
|
+
}
|
|
21
|
+
/** Page backwards through history up to `maximum` records, validating cursors and IDs. */
|
|
22
|
+
export async function loadHistory(page, maximum = HISTORY_LIMIT) {
|
|
23
|
+
if (!Number.isInteger(maximum) || maximum < 1)
|
|
24
|
+
throw new Error('Invalid history bound');
|
|
25
|
+
const newest = [];
|
|
26
|
+
const ids = new Set();
|
|
27
|
+
const cursors = new Set();
|
|
28
|
+
let before;
|
|
29
|
+
let truncated = false;
|
|
30
|
+
while (true) {
|
|
31
|
+
const limit = Math.min(100, maximum - newest.length);
|
|
32
|
+
const result = await page({ order: 'desc', limit, ...(before ? { before } : {}) });
|
|
33
|
+
if (!Array.isArray(result.messages) || result.messages.length > limit)
|
|
34
|
+
throw new Error('Invalid or oversized history page');
|
|
35
|
+
for (const message of result.messages) {
|
|
36
|
+
if (!message.id || ids.has(message.id))
|
|
37
|
+
throw new Error('History pagination returned duplicate or missing message IDs');
|
|
38
|
+
ids.add(message.id);
|
|
39
|
+
newest.push(message);
|
|
40
|
+
}
|
|
41
|
+
if (result.hasMore === false)
|
|
42
|
+
break;
|
|
43
|
+
if (!result.messages.length) {
|
|
44
|
+
if (result.hasMore === true)
|
|
45
|
+
throw new Error('History pagination reported more data but returned no messages');
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (newest.length >= maximum) {
|
|
49
|
+
truncated = true;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const cursor = result.nextBefore ?? result.messages.at(-1)?.id;
|
|
53
|
+
if (!cursor || cursors.has(cursor) || cursor === before)
|
|
54
|
+
throw new Error('History pagination did not advance');
|
|
55
|
+
cursors.add(cursor);
|
|
56
|
+
before = cursor;
|
|
57
|
+
}
|
|
58
|
+
// Preserve backend order (including equal timestamps and split tool IDs).
|
|
59
|
+
return { messages: newest.reverse(), truncated };
|
|
60
|
+
}
|
|
61
|
+
function textContent(content, user = false) {
|
|
62
|
+
let text = typeof content === 'string' ? content : Array.isArray(content) ? content.map(part => {
|
|
63
|
+
const p = record(part);
|
|
64
|
+
return p?.type === 'text' && typeof p.text === 'string' ? p.text : '';
|
|
65
|
+
}).filter(Boolean).join('\n') : '';
|
|
66
|
+
if (user) {
|
|
67
|
+
// Harness inserts these as ordinary user content on reconnect. Never expose
|
|
68
|
+
// environment, memory, or system internals as if the user had typed them.
|
|
69
|
+
text = text.replace(/<system-reminder\b[^>]*>[\s\S]*?<\/system-reminder>/gi, '').replace(/<system-reminder\b[\s\S]*$/gi, '');
|
|
70
|
+
try {
|
|
71
|
+
const envelope = JSON.parse(text);
|
|
72
|
+
if (envelope && typeof envelope === 'object' && typeof envelope.type === 'string') {
|
|
73
|
+
if (envelope.type === 'user_message' && typeof envelope.message === 'string')
|
|
74
|
+
text = envelope.message;
|
|
75
|
+
else if (['heartbeat', 'system_message', 'system_alert', 'memory_warning'].includes(envelope.type))
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { /* ordinary human text */ }
|
|
80
|
+
}
|
|
81
|
+
return sanitizeText(text).trim();
|
|
82
|
+
}
|
|
83
|
+
/** Text shown for a user image that cannot be displayed (unsupported, invalid, or over the display budget). */
|
|
84
|
+
export const IMAGE_PLACEHOLDER = '[Image]';
|
|
85
|
+
/** Most decoded image bytes restored for display from one conversation, newest first. Older images become placeholders. */
|
|
86
|
+
export const HISTORY_IMAGE_BUDGET = 48 * 1024 * 1024;
|
|
87
|
+
/** Letta `ImageContent` items of a backend user record, in order. */
|
|
88
|
+
function imageItems(content) {
|
|
89
|
+
return Array.isArray(content) ? content.map(record).filter((p) => p?.type === 'image') : [];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Display part for one stored image: a `data:` URL for a well-formed PNG,
|
|
93
|
+
* JPEG, GIF or WebP within budget, otherwise the {@link IMAGE_PLACEHOLDER}.
|
|
94
|
+
* The bytes are re-checked, so a mislabelled record never becomes a data URL
|
|
95
|
+
* of another type.
|
|
96
|
+
*/
|
|
97
|
+
function imagePart(item, budget) {
|
|
98
|
+
const source = record(item.source);
|
|
99
|
+
if (budget.left > 0 && source?.type === 'base64' && typeof source.data === 'string' && typeof source.media_type === 'string') {
|
|
100
|
+
try {
|
|
101
|
+
const image = decodeImagePart({ type: 'image', image: source.data, mediaType: source.media_type });
|
|
102
|
+
if (image.bytes <= budget.left) {
|
|
103
|
+
budget.left -= image.bytes;
|
|
104
|
+
return { type: 'file', mediaType: image.mediaType, url: `data:${image.mediaType};base64,${image.base64}` };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch { /* not displayable */ }
|
|
108
|
+
}
|
|
109
|
+
return { type: 'text', text: IMAGE_PLACEHOLDER };
|
|
110
|
+
}
|
|
111
|
+
/** Display-only message time from the backend record, when it is a valid date. */
|
|
112
|
+
function timestamp(row) {
|
|
113
|
+
const time = typeof row.date === 'string' ? Date.parse(row.date) : NaN;
|
|
114
|
+
return Number.isFinite(time) ? { metadata: { createdAt: new Date(time).toISOString() } } : {};
|
|
115
|
+
}
|
|
116
|
+
/** Display projection only. Never reconstruct the model's context from this.
|
|
117
|
+
* Completed allowlisted app tools become inert output cards; everything else is
|
|
118
|
+
* omitted, including pending approvals, reasoning, system, memory and events.
|
|
119
|
+
* User images become `file` parts with a `data:` URL (newest first, within
|
|
120
|
+
* `imageBudget` bytes); the rest become an `[Image]` text placeholder.
|
|
121
|
+
*/
|
|
122
|
+
export function projectHistory(messages, appTools, imageBudget = HISTORY_IMAGE_BUDGET) {
|
|
123
|
+
const returns = new Map();
|
|
124
|
+
for (const message of messages) {
|
|
125
|
+
const row = message;
|
|
126
|
+
if (row.message_type === 'tool_return_message' && typeof row.tool_call_id === 'string')
|
|
127
|
+
returns.set(row.tool_call_id, row);
|
|
128
|
+
}
|
|
129
|
+
// Spend the image budget on the newest images first.
|
|
130
|
+
const images = new Map();
|
|
131
|
+
const budget = { left: imageBudget };
|
|
132
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
133
|
+
const row = messages[index];
|
|
134
|
+
if (row.message_type !== 'user_message')
|
|
135
|
+
continue;
|
|
136
|
+
const items = imageItems(row.content);
|
|
137
|
+
if (items.length)
|
|
138
|
+
images.set(messages[index].id, items.reverse().map(item => imagePart(item, budget)).reverse());
|
|
139
|
+
}
|
|
140
|
+
const shownCalls = new Set();
|
|
141
|
+
const projected = [];
|
|
142
|
+
for (const message of messages) {
|
|
143
|
+
const row = message;
|
|
144
|
+
if (row.message_type === 'user_message' || row.message_type === 'assistant_message') {
|
|
145
|
+
const role = row.message_type === 'user_message' ? 'user' : 'assistant';
|
|
146
|
+
const text = textContent(row.content, role === 'user');
|
|
147
|
+
const attached = role === 'user' ? images.get(message.id) ?? [] : [];
|
|
148
|
+
if (text || attached.length)
|
|
149
|
+
projected.push({ id: `history-${message.id}`, role, parts: [...(text ? [{ type: 'text', text }] : []), ...attached], ...timestamp(row) });
|
|
150
|
+
}
|
|
151
|
+
else if (row.message_type === 'tool_call_message' || row.message_type === 'approval_request_message') {
|
|
152
|
+
const call = record(row.tool_call);
|
|
153
|
+
const id = call?.tool_call_id;
|
|
154
|
+
const name = call?.name;
|
|
155
|
+
if (!call || typeof id !== 'string' || typeof name !== 'string' || !appTools.includes(name) || shownCalls.has(id))
|
|
156
|
+
continue;
|
|
157
|
+
const output = returns.get(id);
|
|
158
|
+
if (!output || !['success', 'error'].includes(String(output.status)))
|
|
159
|
+
continue;
|
|
160
|
+
let input;
|
|
161
|
+
try {
|
|
162
|
+
input = typeof call.arguments === 'string' ? JSON.parse(sanitizeText(call.arguments)) : JSON.parse(sanitizeText(JSON.stringify(call.arguments)));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const text = textContent(output.tool_return);
|
|
168
|
+
let value = text;
|
|
169
|
+
try {
|
|
170
|
+
value = JSON.parse(text);
|
|
171
|
+
}
|
|
172
|
+
catch { /* text output */ }
|
|
173
|
+
shownCalls.add(id);
|
|
174
|
+
projected.push({ id: `history-${message.id}`, role: 'assistant', ...timestamp(row), parts: [{
|
|
175
|
+
type: 'dynamic-tool', toolName: name, toolCallId: id, input,
|
|
176
|
+
providerExecuted: true,
|
|
177
|
+
...(output.status === 'error' ? { state: 'output-error', errorText: text } : { state: 'output-available', output: value }),
|
|
178
|
+
}] });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return projected;
|
|
182
|
+
}
|
|
183
|
+
/** @throws when the conversation ends with an unanswered user turn or an unfinished tool call. */
|
|
184
|
+
export function assertHistorySettled(messages) {
|
|
185
|
+
// Conservative: do not resume an abandoned prompt or half-completed tool chain.
|
|
186
|
+
let pendingUser = false;
|
|
187
|
+
const calls = new Set();
|
|
188
|
+
for (const message of messages) {
|
|
189
|
+
const row = message;
|
|
190
|
+
if (row.message_type === 'user_message' && (textContent(row.content, true) || imageItems(row.content).length))
|
|
191
|
+
pendingUser = true;
|
|
192
|
+
if (row.message_type === 'assistant_message' && textContent(row.content))
|
|
193
|
+
pendingUser = false;
|
|
194
|
+
if (row.message_type === 'tool_call_message' || row.message_type === 'approval_request_message') {
|
|
195
|
+
const id = record(row.tool_call)?.tool_call_id;
|
|
196
|
+
if (typeof id === 'string')
|
|
197
|
+
calls.add(id);
|
|
198
|
+
}
|
|
199
|
+
if (row.message_type === 'tool_return_message' && typeof row.tool_call_id === 'string')
|
|
200
|
+
calls.delete(row.tool_call_id);
|
|
201
|
+
}
|
|
202
|
+
if (pendingUser || calls.size)
|
|
203
|
+
throw new Error('Conversation history has an unfinished or uncertain turn. Inspect backend; no implicit retry or repair. Select another conversation to continue.');
|
|
204
|
+
}
|
|
205
|
+
/** List every conversation of one agent, failing closed on foreign or repeated rows. */
|
|
206
|
+
export async function listConversations(list, agentId) {
|
|
207
|
+
const result = [];
|
|
208
|
+
const ids = new Set();
|
|
209
|
+
let after;
|
|
210
|
+
while (true) {
|
|
211
|
+
const page = await list({ agentId, limit: 100, order: 'asc', orderBy: 'createdAt', ...(after ? { after } : {}) });
|
|
212
|
+
if (!page.length)
|
|
213
|
+
break;
|
|
214
|
+
for (const conversation of page) {
|
|
215
|
+
if (conversation.agent_id !== agentId || ids.has(conversation.id))
|
|
216
|
+
throw new Error('Conversation listing returned wrong agent or non-advancing cursor');
|
|
217
|
+
ids.add(conversation.id);
|
|
218
|
+
result.push(conversation);
|
|
219
|
+
}
|
|
220
|
+
after = page.at(-1).id;
|
|
221
|
+
if (result.length > 10_000)
|
|
222
|
+
throw new Error('Conversation list exceeds 10,000; refusing to silently truncate');
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Persisted mapping from a logical definition ID to the Letta-generated agent ID. */
|
|
2
|
+
export type Identity = {
|
|
3
|
+
version: 2;
|
|
4
|
+
definitionId: string;
|
|
5
|
+
name: string;
|
|
6
|
+
backend: string;
|
|
7
|
+
agentId: string;
|
|
8
|
+
conversationId: string;
|
|
9
|
+
};
|
|
10
|
+
/** Letta conversation IDs accepted by this library (`default` is the agent's default conversation). */
|
|
11
|
+
export declare const validConversationId: (id: string) => boolean;
|
|
12
|
+
/** Backend operations used while acquiring an identity. */
|
|
13
|
+
export interface IdentityBackend {
|
|
14
|
+
/** Create the Letta agent. Called at most once per logical ID, ever. */
|
|
15
|
+
create(): Promise<string>;
|
|
16
|
+
/** Confirm the mapped agent still exists and matches; throw otherwise. */
|
|
17
|
+
validate(id: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/** Handle returned by {@link acquireIdentity}. Holds an exclusive lock until `release()`. */
|
|
20
|
+
export type IdentityLease = Awaited<ReturnType<typeof acquireIdentity>>;
|
|
21
|
+
/**
|
|
22
|
+
* Acquire the durable identity for a definition, creating the Letta agent on
|
|
23
|
+
* first use. Fails closed on stale locks, uncertain creations or mismatches:
|
|
24
|
+
* it never searches by name, recreates, or retries.
|
|
25
|
+
*
|
|
26
|
+
* Files in `directory` (all 0600, directory 0700):
|
|
27
|
+
* - `<id>.json` the mapping; `<id>.lock` the process lock;
|
|
28
|
+
* - `<id>.pending.json` / `<id>.conversation.pending.json` uncertain creations;
|
|
29
|
+
* - `<id>.<conversation>.turn.pending.json` a turn whose delivery is not yet confirmed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function acquireIdentity(directory: string, definition: {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
}, backend: string, api: IdentityBackend): Promise<{
|
|
35
|
+
identity: Identity;
|
|
36
|
+
release: () => void;
|
|
37
|
+
selectConversation: (id: string) => void;
|
|
38
|
+
createConversation: (create: (agentId: string) => Promise<string>) => Promise<string>;
|
|
39
|
+
assertNoPendingTurn: (id: string) => void;
|
|
40
|
+
beginTurn: (id: string) => void;
|
|
41
|
+
completeTurn: (id: string) => void;
|
|
42
|
+
}>;
|
|
43
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAGA,sFAAsF;AACtF,MAAM,MAAM,QAAQ,GAAG;IAAE,OAAO,EAAE,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpI,uGAAuG;AACvG,eAAO,MAAM,mBAAmB,GAAI,IAAI,MAAM,KAAG,OAA8E,CAAC;AAGhI,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe;;;6BA4DpG,MAAM;iCAII,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC;8BAc7C,MAAM;oBAGhB,MAAM;uBAKH,MAAM;GAOnC"}
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { chmodSync, closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/** Letta conversation IDs accepted by this library (`default` is the agent's default conversation). */
|
|
4
|
+
export const validConversationId = (id) => id === 'default' || /^(?:conv-|local-conv-)[a-zA-Z0-9-]+$/.test(id);
|
|
5
|
+
const validLocalAgentId = (id) => typeof id === 'string' && /^agent-local-[a-zA-Z0-9-]+$/.test(id);
|
|
6
|
+
/**
|
|
7
|
+
* Acquire the durable identity for a definition, creating the Letta agent on
|
|
8
|
+
* first use. Fails closed on stale locks, uncertain creations or mismatches:
|
|
9
|
+
* it never searches by name, recreates, or retries.
|
|
10
|
+
*
|
|
11
|
+
* Files in `directory` (all 0600, directory 0700):
|
|
12
|
+
* - `<id>.json` the mapping; `<id>.lock` the process lock;
|
|
13
|
+
* - `<id>.pending.json` / `<id>.conversation.pending.json` uncertain creations;
|
|
14
|
+
* - `<id>.<conversation>.turn.pending.json` a turn whose delivery is not yet confirmed.
|
|
15
|
+
*/
|
|
16
|
+
export async function acquireIdentity(directory, definition, backend, api) {
|
|
17
|
+
if (!/^[a-z0-9-]+$/.test(definition.id))
|
|
18
|
+
throw new Error('Invalid logical agent identity');
|
|
19
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
20
|
+
if (lstatSync(directory).isSymbolicLink())
|
|
21
|
+
throw new Error('Unsafe identity directory');
|
|
22
|
+
chmodSync(directory, 0o700);
|
|
23
|
+
const base = join(directory, definition.id);
|
|
24
|
+
const lock = `${base}.lock`;
|
|
25
|
+
let fd;
|
|
26
|
+
try {
|
|
27
|
+
fd = openSync(lock, 'wx', 0o600);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error(`Agent identity is locked: ${lock}. Another process may be running. After a crash, verify its PID is no longer running before removing the lock; never remove a pending intent blindly.`);
|
|
31
|
+
}
|
|
32
|
+
writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
|
|
33
|
+
fsyncSync(fd);
|
|
34
|
+
closeSync(fd);
|
|
35
|
+
let released = false;
|
|
36
|
+
const release = () => { if (!released) {
|
|
37
|
+
unlinkSync(lock);
|
|
38
|
+
released = true;
|
|
39
|
+
} };
|
|
40
|
+
const file = `${base}.json`;
|
|
41
|
+
const pending = `${base}.pending.json`;
|
|
42
|
+
const syncDirectory = () => { const dir = openSync(directory, 'r'); try {
|
|
43
|
+
fsyncSync(dir);
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
closeSync(dir);
|
|
47
|
+
} };
|
|
48
|
+
const exists = (path) => {
|
|
49
|
+
try {
|
|
50
|
+
lstatSync(path);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code === 'ENOENT')
|
|
55
|
+
return false;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const read = (path) => {
|
|
60
|
+
if (!lstatSync(path).isFile() || lstatSync(path).isSymbolicLink())
|
|
61
|
+
throw new Error('Unsafe identity file');
|
|
62
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
63
|
+
};
|
|
64
|
+
const durableWrite = (path, value) => {
|
|
65
|
+
const handle = openSync(path, 'wx', 0o600);
|
|
66
|
+
try {
|
|
67
|
+
writeFileSync(handle, JSON.stringify(value, null, 2));
|
|
68
|
+
fsyncSync(handle);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
closeSync(handle);
|
|
72
|
+
}
|
|
73
|
+
syncDirectory();
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
if (exists(pending))
|
|
77
|
+
throw new Error(`Unresolved agent creation intent: ${pending}. Inspect the local backend and reconcile manually; refusing to create a duplicate.`);
|
|
78
|
+
const conversationPending = `${base}.conversation.pending.json`;
|
|
79
|
+
if (exists(conversationPending))
|
|
80
|
+
throw new Error(`Unresolved conversation creation intent: ${conversationPending}. Reconcile with backend before continuing; refusing to create a duplicate.`);
|
|
81
|
+
let identity;
|
|
82
|
+
let migrate = false;
|
|
83
|
+
const save = () => {
|
|
84
|
+
if (released)
|
|
85
|
+
throw new Error('Identity lock already released');
|
|
86
|
+
durableWrite(`${file}.new`, identity);
|
|
87
|
+
renameSync(`${file}.new`, file);
|
|
88
|
+
syncDirectory();
|
|
89
|
+
};
|
|
90
|
+
if (exists(file)) {
|
|
91
|
+
const stored = read(file);
|
|
92
|
+
if (![1, 2].includes(stored.version) || stored.definitionId !== definition.id || stored.name !== definition.name || stored.backend !== backend || typeof stored.conversationId !== 'string' || !validConversationId(stored.conversationId) || (stored.version === 1 && stored.conversationId !== 'default') || !validLocalAgentId(stored.agentId))
|
|
93
|
+
throw new Error('Invalid identity mapping or backend mismatch; refusing to recreate agent');
|
|
94
|
+
migrate = stored.version === 1;
|
|
95
|
+
identity = { ...stored, version: 2 };
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
durableWrite(pending, { definitionId: definition.id, name: definition.name, backend, createdAt: new Date().toISOString(), state: 'creation-uncertain' });
|
|
99
|
+
const agentId = await api.create();
|
|
100
|
+
if (!validLocalAgentId(agentId))
|
|
101
|
+
throw new Error('SDK returned a non-local agent ID');
|
|
102
|
+
identity = { version: 2, definitionId: definition.id, name: definition.name, backend, agentId, conversationId: 'default' };
|
|
103
|
+
// Keep the intent until an fsynced mapping is in place. Crashes fail closed.
|
|
104
|
+
durableWrite(`${file}.new`, identity);
|
|
105
|
+
renameSync(`${file}.new`, file);
|
|
106
|
+
syncDirectory();
|
|
107
|
+
unlinkSync(pending);
|
|
108
|
+
}
|
|
109
|
+
await api.validate(identity.agentId);
|
|
110
|
+
if (migrate)
|
|
111
|
+
save();
|
|
112
|
+
const selectConversation = (id) => {
|
|
113
|
+
if (!validConversationId(id))
|
|
114
|
+
throw new Error('Invalid conversation ID');
|
|
115
|
+
identity.conversationId = id;
|
|
116
|
+
save();
|
|
117
|
+
};
|
|
118
|
+
const createConversation = async (create) => {
|
|
119
|
+
if (released || exists(conversationPending))
|
|
120
|
+
throw new Error('Unresolved conversation creation or released lock');
|
|
121
|
+
durableWrite(conversationPending, { agentId: identity.agentId, createdAt: new Date().toISOString(), state: 'creation-uncertain' });
|
|
122
|
+
const id = await create(identity.agentId);
|
|
123
|
+
if (id === 'default' || !validConversationId(id))
|
|
124
|
+
throw new Error('Invalid created conversation');
|
|
125
|
+
selectConversation(id);
|
|
126
|
+
unlinkSync(conversationPending);
|
|
127
|
+
syncDirectory();
|
|
128
|
+
return id;
|
|
129
|
+
};
|
|
130
|
+
const turnPath = (id) => {
|
|
131
|
+
if (!validConversationId(id))
|
|
132
|
+
throw new Error('Invalid conversation ID');
|
|
133
|
+
return `${base}.${id}.turn.pending.json`;
|
|
134
|
+
};
|
|
135
|
+
const assertNoPendingTurn = (id) => {
|
|
136
|
+
if (exists(turnPath(id)))
|
|
137
|
+
throw new Error(`Uncertain prior delivery: ${turnPath(id)}. Inspect backend and reconcile manually; no automatic replay. Select another conversation to continue.`);
|
|
138
|
+
};
|
|
139
|
+
const beginTurn = (id) => {
|
|
140
|
+
if (released)
|
|
141
|
+
throw new Error('Identity lock already released');
|
|
142
|
+
assertNoPendingTurn(id);
|
|
143
|
+
durableWrite(turnPath(id), { agentId: identity.agentId, conversationId: id, createdAt: new Date().toISOString(), state: 'delivery-uncertain' });
|
|
144
|
+
};
|
|
145
|
+
const completeTurn = (id) => {
|
|
146
|
+
if (released)
|
|
147
|
+
throw new Error('Identity lock already released');
|
|
148
|
+
unlinkSync(turnPath(id));
|
|
149
|
+
syncDirectory();
|
|
150
|
+
};
|
|
151
|
+
return { identity, release, selectConversation, createConversation, assertNoPendingTurn, beginTurn, completeTurn };
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
release();
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
|
package/dist/images.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ImageContent } from '@letta-ai/letta-agent-sdk';
|
|
2
|
+
/** Image types a user turn may carry. Letta's `ImageContent` accepts exactly these. */
|
|
3
|
+
export declare const IMAGE_MEDIA_TYPES: readonly ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
4
|
+
export type ImageMediaType = typeof IMAGE_MEDIA_TYPES[number];
|
|
5
|
+
/**
|
|
6
|
+
* Bounds for images in one user turn (decoded bytes). They keep requests,
|
|
7
|
+
* backend history and restored views bounded; clients should downscale before
|
|
8
|
+
* sending (the browser app does).
|
|
9
|
+
*/
|
|
10
|
+
export interface ImageLimits {
|
|
11
|
+
maxImageBytes: number;
|
|
12
|
+
maxImages: number;
|
|
13
|
+
maxTotalBytes: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const IMAGE_LIMITS: Readonly<ImageLimits>;
|
|
16
|
+
/** Machine-readable reason for {@link ImageInputError}. */
|
|
17
|
+
export type ImageInputErrorCode = 'image_unsupported_type' | 'image_invalid' | 'image_remote_url' | 'image_too_large' | 'images_too_many' | 'images_too_large';
|
|
18
|
+
/** A rejected image input. `code` is stable; `message` is human-readable. */
|
|
19
|
+
export declare class ImageInputError extends Error {
|
|
20
|
+
readonly code: ImageInputErrorCode;
|
|
21
|
+
readonly name = "ImageInputError";
|
|
22
|
+
constructor(code: ImageInputErrorCode, message: string);
|
|
23
|
+
}
|
|
24
|
+
/** Reference provider key used for images in the in-memory transcript. */
|
|
25
|
+
export declare const IMAGE_REFERENCE_PROVIDER = "ai-sdk-letta-sha256";
|
|
26
|
+
/** Detect PNG, JPEG, GIF or WebP from magic bytes; `undefined` otherwise. */
|
|
27
|
+
export declare function sniffImageType(bytes: Uint8Array): ImageMediaType | undefined;
|
|
28
|
+
/** A validated image, ready for Letta. */
|
|
29
|
+
export interface DecodedImage {
|
|
30
|
+
mediaType: ImageMediaType;
|
|
31
|
+
base64: string;
|
|
32
|
+
bytes: number;
|
|
33
|
+
sha256: string;
|
|
34
|
+
}
|
|
35
|
+
/** An image-carrying user content part, as found in AI SDK model messages. */
|
|
36
|
+
type PartLike = {
|
|
37
|
+
type: string;
|
|
38
|
+
image?: unknown;
|
|
39
|
+
data?: unknown;
|
|
40
|
+
mediaType?: unknown;
|
|
41
|
+
};
|
|
42
|
+
/** True for AI SDK `image` parts and `file` parts whose media type is an image. */
|
|
43
|
+
export declare function isImagePart(part: PartLike): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Validate one AI SDK `image` or `file` part and decode it. Accepts base64
|
|
46
|
+
* strings, `data:` URLs (string or `URL`), bytes, and the tagged
|
|
47
|
+
* `{ type: 'data' | 'url' }` file shapes. Remote URLs are rejected: images are
|
|
48
|
+
* never fetched.
|
|
49
|
+
* @throws {ImageInputError}
|
|
50
|
+
*/
|
|
51
|
+
export declare function decodeImagePart(part: PartLike, limit?: number): DecodedImage;
|
|
52
|
+
/** Enforce count and combined size over already-decoded images. @throws {ImageInputError} */
|
|
53
|
+
export declare function assertImageBudget(images: readonly {
|
|
54
|
+
bytes: number;
|
|
55
|
+
}[], limits?: Readonly<ImageLimits>): void;
|
|
56
|
+
/**
|
|
57
|
+
* Validate a list of base64 images (as received over HTTP) against every
|
|
58
|
+
* limit. Returns decoded images with their content hash.
|
|
59
|
+
* @throws {ImageInputError}
|
|
60
|
+
*/
|
|
61
|
+
export declare function validateImages(images: readonly {
|
|
62
|
+
mediaType?: unknown;
|
|
63
|
+
data?: unknown;
|
|
64
|
+
}[], limits?: Readonly<ImageLimits>): DecodedImage[];
|
|
65
|
+
/** Letta `ImageContent` for a decoded image. */
|
|
66
|
+
export declare const toLettaImage: (image: DecodedImage) => ImageContent;
|
|
67
|
+
/**
|
|
68
|
+
* Content hash of an image part, for history comparison. Compact transcript
|
|
69
|
+
* references (see {@link compactImagePart}) yield their stored hash; anything
|
|
70
|
+
* else is decoded and hashed.
|
|
71
|
+
*/
|
|
72
|
+
export declare function imagePartDigest(part: PartLike): string;
|
|
73
|
+
/** Replace an image part's data with a content-hash reference, so transcripts never hold image bytes twice. */
|
|
74
|
+
export declare function compactImagePart(part: PartLike): {
|
|
75
|
+
type: 'file';
|
|
76
|
+
mediaType: string;
|
|
77
|
+
data: {
|
|
78
|
+
type: 'reference';
|
|
79
|
+
reference: Record<string, string>;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export {};
|
|
83
|
+
//# sourceMappingURL=images.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../src/images.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,iEAAkE,CAAC;AACjG,MAAM,MAAM,cAAc,GAAG,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAAG,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;AAChG,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,WAAW,CAO7C,CAAC;AAEH,2DAA2D;AAC3D,MAAM,MAAM,mBAAmB,GAC3B,wBAAwB,GACxB,eAAe,GACf,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,CAAC;AAEvB,6EAA6E;AAC7E,qBAAa,eAAgB,SAAQ,KAAK;IAE5B,QAAQ,CAAC,IAAI,EAAE,mBAAmB;IAD9C,SAAkB,IAAI,qBAAqB;gBACtB,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM;CAChE;AAID,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAE9D,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,CAO5E;AAaD,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAAG,SAAS,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AA8B1G,8EAA8E;AAC9E,KAAK,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAYvF,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,GAAE,MAAmC,GAAG,YAAY,CAmBxG;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,MAAM,GAAE,QAAQ,CAAC,WAAW,CAAgB,GAAG,IAAI,CAI1H;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,EAAE,MAAM,GAAE,QAAQ,CAAC,WAAW,CAAgB,GAAG,YAAY,EAAE,CASvJ;AAED,gDAAgD;AAChD,eAAO,MAAM,YAAY,GAAI,OAAO,YAAY,KAAG,YAAgH,CAAC;AAEpK;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEtD;AAED,+GAA+G;AAC/G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;CAAE,CAMpJ"}
|