@cubos/agent-sdk 0.0.1136563

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.
@@ -0,0 +1,263 @@
1
+ import type { Schemas } from "./schemas.js";
2
+ /** A conversation between one end user and one agent. */
3
+ export interface Conversation {
4
+ id: string;
5
+ /** What to show: the name a human gave it, or the one the engine derived from
6
+ * the opening turns. Null until there is anything nameable in it. */
7
+ title: string | null;
8
+ /** True when `title` came from the engine rather than from a person. */
9
+ titleIsGenerated: boolean;
10
+ /** Slug of the agent answering, or null for a conversation opened without one. */
11
+ agentSlug: string | null;
12
+ /** Sorts the chat list; bumped by any activity, including the agent's own. */
13
+ lastActivityAt: string;
14
+ archived: boolean;
15
+ /** A replica is actively running a turn right now. */
16
+ isProcessing: boolean;
17
+ /** There is an unanswered user message. True before `isProcessing` flips. */
18
+ hasPendingTurn: boolean;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }
22
+ export type MessageRole = "user" | "agent";
23
+ export type AttachmentKind = "image" | "audio";
24
+ /** Media the user sent with a message. Fetch the bytes with
25
+ * `fetchAttachment(conversationId, message.id, attachment.id)`. */
26
+ export interface Attachment {
27
+ id: string;
28
+ kind: AttachmentKind;
29
+ mimeType: string;
30
+ /** Size in bytes, so a UI can decide before downloading. */
31
+ bytes: number;
32
+ /** Name the sender gave an image, which the model also sees next to it. Null
33
+ * when unnamed, and always null for audio. */
34
+ label: string | null;
35
+ }
36
+ /** One visible turn of the conversation. Tool calls and internal bookkeeping
37
+ * events are not messages and never surface here. */
38
+ /** The component libraries enabled on a conversation, and what they add up to.
39
+ *
40
+ * A library is authored by the operator — its entries reach the model verbatim,
41
+ * so writing one needs an api_key. A client picks which of them apply to the
42
+ * screen it has open, and gets back `tags`: every component the agent may now
43
+ * instantiate, in the order it sees them indexed.
44
+ *
45
+ * `tags` is the half worth acting on. A tag with no renderer in your app is a
46
+ * block the agent will happily write and nothing will draw — a silent hole in a
47
+ * reply — and this response is the only place the catalog and your component map
48
+ * meet. The reverse is fine and expected: renderers with no tag are what draws
49
+ * the components of older messages, after a library stopped offering them. */
50
+ export interface EnabledComponents {
51
+ libraries: string[];
52
+ tags: string[];
53
+ }
54
+ /** A rendered segment of an agent message.
55
+ *
56
+ * `markdown` carries the raw markdown of that stretch of the reply; `component`
57
+ * is one of the components registered on the conversation, with props already
58
+ * parsed and validated. Anything the client doesn't recognise should fall back
59
+ * to rendering nothing rather than the tag text — but that only happens for a
60
+ * component dropped from the set after the message was written.
61
+ *
62
+ * A block that is not a `component` always has `text`. Future block kinds will
63
+ * keep that guarantee, so rendering `text` is always a safe fallback. */
64
+ export type Block = {
65
+ type: "markdown";
66
+ text: string;
67
+ } | {
68
+ type: "component";
69
+ tag: string;
70
+ props: Record<string, unknown>;
71
+ };
72
+ export interface Message {
73
+ id: string;
74
+ role: MessageRole;
75
+ /** An image's caption, or the transcript of a voice note. Empty for an
76
+ * unlabelled image, and for a voice note until STT has run, so render
77
+ * `attachments` even when this is blank. */
78
+ content: string;
79
+ /** Media the user attached, in upload order: the images of one message, or
80
+ * the single clip of a voice note. */
81
+ attachments: Attachment[];
82
+ /** Voice notes only: whether STT has finished. False while the clip waits,
83
+ * true once its transcription landed — *including* when it landed empty,
84
+ * which is what a silent recording produces. Without this a client cannot
85
+ * tell "still transcribing" from "transcribed, and there was no speech":
86
+ * `content` is the empty string in both. */
87
+ transcribed?: boolean;
88
+ /** Per-conversation ordering. Stable across reconnects. */
89
+ seq: number;
90
+ at: string;
91
+ /** The agent's reply split into renderable segments — present on agent
92
+ * messages only. Plain prose is a single `markdown` block, so a client that
93
+ * renders blocks needs no special case for "no components here". */
94
+ blocks?: Block[];
95
+ }
96
+ /** A client tool the agent is waiting on. The arguments are not here: the
97
+ * runner reads them from the pending list, which is also what makes a call
98
+ * survive a client that reconnects after the frame went out. */
99
+ export interface ClientToolCall {
100
+ toolCallId: string;
101
+ toolName: string;
102
+ }
103
+ /**
104
+ * A tool the agent used, for showing its work — "how I got here".
105
+ *
106
+ * Two events make one of these: the call and its result. A **client tool**
107
+ * reports both. A server-side tool reports only its result, so the arguments
108
+ * are read off the `llm_call` that issued it — which means `arguments` is
109
+ * populated for every tool, but only once that call has been seen. A client
110
+ * that connects mid-turn, or pages back to a result whose call is on an older
111
+ * page, gets the entry with none until the page carrying it loads. Treat it as
112
+ * optional, not as a promise.
113
+ *
114
+ * The name is the agent's, not a label: map the ones worth showing to your own
115
+ * wording and hide the rest, or the trail reads like a stack trace.
116
+ */
117
+ export interface ToolActivity {
118
+ toolCallId: string;
119
+ toolName: string;
120
+ /** What the agent passed. Undefined until the call that carries them has
121
+ * been seen — see above. */
122
+ arguments?: Record<string, unknown>;
123
+ /** `running` until the result lands, which is also how it arrives live. */
124
+ status: "running" | "ok" | "error";
125
+ /** Whatever the tool returned, shaped by the tool itself. */
126
+ result?: unknown;
127
+ /** Set when `status` is `error`. */
128
+ error?: string;
129
+ /** Server-measured wall clock of the call. */
130
+ durationMs?: number;
131
+ /** Log order, so a trail can be placed against the messages around it. */
132
+ seq: number;
133
+ at: string;
134
+ }
135
+ export type TodoStatus = "pending" | "in_progress" | "completed";
136
+ /** The agent's own plan for a multi-step request, when the agent chose to keep
137
+ * one. Nothing forces it to, so an empty list is normal. */
138
+ export interface Todo {
139
+ title: string;
140
+ status: TodoStatus;
141
+ }
142
+ /**
143
+ * The plan as it stood at one point in the log.
144
+ *
145
+ * `update_todo` overwrites the whole list on every call, so the log holds one
146
+ * of these per revision. A client that wants "the plan behind this answer"
147
+ * takes the last snapshot before the message and ignores the rest: the
148
+ * intermediate revisions are how the agent got there, not what it decided.
149
+ */
150
+ export interface PlanSnapshot {
151
+ todos: Todo[];
152
+ seq: number;
153
+ }
154
+ /** What the agent is doing right now — the "typing…" signal. */
155
+ export interface Activity {
156
+ isProcessing: boolean;
157
+ hasPendingTurn: boolean;
158
+ }
159
+ export interface ListConversationsOptions {
160
+ /** Page size, 1..=200. Defaults to the server's 30. */
161
+ limit?: number;
162
+ /** Cursor from a previous page's `nextCursor`. */
163
+ before?: string;
164
+ signal?: AbortSignal;
165
+ }
166
+ export interface Page<T> {
167
+ items: T[];
168
+ /** Pass as `before` to fetch the next (older) page; null when exhausted. */
169
+ nextCursor: string | null;
170
+ }
171
+ /** One page of history plus the cursors needed to page backwards and to start a
172
+ * subscription from where the page ends. */
173
+ /**
174
+ * One row of the conversation's event log, exactly as the server sends it.
175
+ *
176
+ * The public conversation surface (`Message`, `Todo`, `Activity`) is curated and
177
+ * stable across internal refactors. This is the other side of that promise: it
178
+ * is the server's own DTO, so a new event type or field appears here the moment
179
+ * the server ships one. That is the right trade for an operator console or an
180
+ * audit view, and the wrong one for a chat bubble.
181
+ *
182
+ * It is not a lower tier of access — the REST API already returns exactly this,
183
+ * so nothing is being unlocked. What differs is only which promise you are
184
+ * holding.
185
+ */
186
+ export type ConversationEvent = Schemas["ConversationEvent"];
187
+ export interface EventPage {
188
+ /** Oldest first. */
189
+ events: ConversationEvent[];
190
+ /** Pass as `before` to fetch the page before this one; null on an empty
191
+ * page. */
192
+ oldestSeq: number | null;
193
+ /** The newest `change_seq` on this page — feed it to `subscribe` as `since`
194
+ * so the stream delivers only what came after. */
195
+ latestChangeSeq: number | null;
196
+ /** The page came back full, so there is probably more behind it. */
197
+ hasOlder: boolean;
198
+ }
199
+ export interface MessagePage {
200
+ /** Oldest first. */
201
+ messages: Message[];
202
+ /** The tools behind those messages, folded and ordered by `seq`. Counted
203
+ * against the page's event `limit` like anything else in the log, which is
204
+ * one more reason a page of events is not a page of messages. */
205
+ toolActivity: ToolActivity[];
206
+ /** Every revision of the plan on this page, ordered by `seq`. */
207
+ plans: PlanSnapshot[];
208
+ /** The `seq` of the newest finished turn on this page, or null if none
209
+ * finished. Compare with the newest message's `seq` to know whether a turn is
210
+ * still running — `isProcessing` alone cannot tell you, because a client tool
211
+ * suspends the turn while it waits. */
212
+ lastTurnDoneSeq: number | null;
213
+ /** Pass as `before` to fetch the page before this one; null on an empty page. */
214
+ oldestSeq: number | null;
215
+ /** Pass as `since` to `subscribe` so the stream delivers only what is newer. */
216
+ latestChangeSeq: number | null;
217
+ /** False when the server returned a short page, meaning the log is exhausted. */
218
+ hasOlder: boolean;
219
+ }
220
+ /** One entry of a workspace directory listing. */
221
+ export interface WorkspaceEntry {
222
+ name: string;
223
+ kind: "file" | "dir" | "symlink";
224
+ /** A file's bytes, or a directory's recursive total. */
225
+ size: number;
226
+ /** Detected content type. Empty for directories, symlinks, and files written
227
+ * before entries carried it. */
228
+ mime: string;
229
+ /** Content hash — of the bytes for a file, of the subtree for a directory.
230
+ * Two entries sharing it are byte-identical, which makes it a cache key. */
231
+ sha256: string | null;
232
+ exec: boolean;
233
+ symlinkTarget: string | null;
234
+ }
235
+ /** A directory of the conversation's files, as it stood at one snapshot. */
236
+ export interface WorkspaceDir {
237
+ /** Absolute path that was listed. */
238
+ path: string;
239
+ entries: WorkspaceEntry[];
240
+ /** The snapshot the listing came from; null when the conversation has no
241
+ * files at all. Pass it around to render a stable view while writes land. */
242
+ rootEventId: string | null;
243
+ rootSeq: number | null;
244
+ }
245
+ export interface CreateConversationOptions {
246
+ /** Which agent to talk to. Must be one the token was minted for; the server
247
+ * refuses anything else. Optional only when the token names exactly one. */
248
+ agentSlug?: string;
249
+ title?: string;
250
+ metadata?: Record<string, unknown>;
251
+ /** Component libraries to enable on the new conversation in the same
252
+ * round-trip. Equivalent to calling `setComponentLibraries` right after. */
253
+ componentLibraries?: string[];
254
+ signal?: AbortSignal;
255
+ }
256
+ /** Identity behind the current token, as the server resolves it. */
257
+ export interface Identity {
258
+ userId: string;
259
+ displayName: string;
260
+ tenantSlug: string;
261
+ /** Agents this token may open a conversation with. */
262
+ agentSlugs: string[];
263
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@cubos/agent-sdk",
3
+ "version": "0.0.1136563",
4
+ "type": "module",
5
+ "description": "Client for the Cubos Agent conversation API. Runs anywhere fetch does.",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "keywords": [
8
+ "cubos",
9
+ "agent",
10
+ "llm",
11
+ "chat",
12
+ "sdk",
13
+ "conversational-ai"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://git.cubos.io/agent/cubos-agent.git",
18
+ "directory": "packages/sdk"
19
+ },
20
+ "homepage": "https://git.cubos.io/agent/cubos-agent/-/tree/main/packages/sdk",
21
+ "bugs": {
22
+ "url": "https://git.cubos.io/agent/cubos-agent/-/issues"
23
+ },
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./sse": {
36
+ "types": "./dist/sse.d.ts",
37
+ "default": "./dist/sse.js"
38
+ }
39
+ },
40
+ "scripts": {
41
+ "build": "bun scripts/build.ts",
42
+ "gen:api": "bun scripts/gen-api.ts",
43
+ "typecheck": "tsgo --noEmit",
44
+ "test": "bun test"
45
+ },
46
+ "devDependencies": {
47
+ "@types/bun": "^1.4.0",
48
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
49
+ "openapi-typescript": "^7.13.0"
50
+ }
51
+ }