@proteos/sdk 0.18.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.
Files changed (94) hide show
  1. package/LICENSE +40 -0
  2. package/dist/chunk-7RGN4E22.cjs +1185 -0
  3. package/dist/chunk-7RGN4E22.cjs.map +1 -0
  4. package/dist/chunk-XJP5WCRZ.js +1125 -0
  5. package/dist/chunk-XJP5WCRZ.js.map +1 -0
  6. package/dist/index.cjs +2384 -0
  7. package/dist/index.cjs.map +1 -0
  8. package/dist/index.d.cts +5225 -0
  9. package/dist/index.d.ts +5225 -0
  10. package/dist/index.js +2146 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/meta/index.cjs +204 -0
  13. package/dist/meta/index.cjs.map +1 -0
  14. package/dist/meta/index.d.cts +2 -0
  15. package/dist/meta/index.d.ts +2 -0
  16. package/dist/meta/index.js +3 -0
  17. package/dist/meta/index.js.map +1 -0
  18. package/dist/types-BNsjfU8N.d.cts +3299 -0
  19. package/dist/types-BNsjfU8N.d.ts +3299 -0
  20. package/package.json +86 -0
  21. package/src/agent/agents.ts +53 -0
  22. package/src/agent/index.ts +134 -0
  23. package/src/agent/mcp-servers.ts +102 -0
  24. package/src/agent/prompts.ts +80 -0
  25. package/src/agent/session-types.ts +397 -0
  26. package/src/agent/sessions.ts +197 -0
  27. package/src/agent/skills.ts +89 -0
  28. package/src/agent/tools.ts +53 -0
  29. package/src/agent/types.ts +362 -0
  30. package/src/auth/index.ts +111 -0
  31. package/src/auth/me.ts +46 -0
  32. package/src/auth/organizations.ts +128 -0
  33. package/src/auth/platform-entities.ts +78 -0
  34. package/src/auth/roles.ts +213 -0
  35. package/src/auth/types.ts +294 -0
  36. package/src/auth/users.ts +226 -0
  37. package/src/client.ts +441 -0
  38. package/src/connector/index.ts +120 -0
  39. package/src/connector/types.ts +150 -0
  40. package/src/conversation/index.ts +297 -0
  41. package/src/conversation/types.ts +590 -0
  42. package/src/conversation/voice.ts +123 -0
  43. package/src/data/index.ts +53 -0
  44. package/src/data/queries.ts +66 -0
  45. package/src/data/records.ts +122 -0
  46. package/src/data/types.ts +89 -0
  47. package/src/errors.ts +148 -0
  48. package/src/events/index.ts +172 -0
  49. package/src/events/types.ts +77 -0
  50. package/src/functions/actions.ts +95 -0
  51. package/src/functions/index.ts +32 -0
  52. package/src/functions/types.ts +71 -0
  53. package/src/http/index.ts +2 -0
  54. package/src/http/query-params.ts +106 -0
  55. package/src/index.ts +598 -0
  56. package/src/iterator.ts +183 -0
  57. package/src/knowledge/graph.ts +35 -0
  58. package/src/knowledge/index.ts +104 -0
  59. package/src/knowledge/labels.ts +70 -0
  60. package/src/knowledge/links.ts +65 -0
  61. package/src/knowledge/nodes.ts +198 -0
  62. package/src/knowledge/record-links.ts +66 -0
  63. package/src/knowledge/types.ts +569 -0
  64. package/src/meta/apps.ts +107 -0
  65. package/src/meta/components.ts +124 -0
  66. package/src/meta/currency/index.ts +202 -0
  67. package/src/meta/entities.ts +193 -0
  68. package/src/meta/filters.ts +76 -0
  69. package/src/meta/index.ts +227 -0
  70. package/src/meta/layout/common-props.ts +93 -0
  71. package/src/meta/layout/control-registry.json +70 -0
  72. package/src/meta/layout/control-registry.ts +92 -0
  73. package/src/meta/layout/elements.ts +203 -0
  74. package/src/meta/layout/index.ts +41 -0
  75. package/src/meta/layout/page-layout.ts +35 -0
  76. package/src/meta/layout/size-value.ts +27 -0
  77. package/src/meta/list-views.ts +109 -0
  78. package/src/meta/lists.ts +104 -0
  79. package/src/meta/menu-configurations.ts +128 -0
  80. package/src/meta/modules.ts +159 -0
  81. package/src/meta/pages.ts +106 -0
  82. package/src/meta/types.ts +1115 -0
  83. package/src/meta/variables.ts +98 -0
  84. package/src/storage/files.ts +183 -0
  85. package/src/storage/index.ts +33 -0
  86. package/src/storage/types.ts +70 -0
  87. package/src/types/common.ts +143 -0
  88. package/src/types/index.ts +28 -0
  89. package/src/types/options.ts +95 -0
  90. package/src/workflow/executions.ts +99 -0
  91. package/src/workflow/index.ts +109 -0
  92. package/src/workflow/node-types.ts +50 -0
  93. package/src/workflow/types.ts +658 -0
  94. package/src/workflow/workflows.ts +152 -0
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Types for the agent-service Sessions API — a conversation with a configured
3
+ * Agent.
4
+ *
5
+ * Hand-written mirror of the Go models in `go.proteos.ai/model/agent`
6
+ * (`session.go`, `session-event.go`, `session-event-type.go`, `session-payload.go`,
7
+ * `session-content.go` + `api/session-requests.go`). Field names match the
8
+ * snake_case wire format exactly, including enum values.
9
+ *
10
+ * A Session is a thin mutable parent over an append-only `SessionEvent` log: the
11
+ * events are the source of truth, the session carries only identity, the agent it
12
+ * runs, a title and the latest lifecycle status. A {@link Turn} is a derived
13
+ * projection of the log (never stored).
14
+ */
15
+
16
+ import type { AuditFields, ListOptions, UserRef } from '../types/common.js'
17
+ import type { ToolKind } from './types.js'
18
+
19
+ export type { ToolKind } from './types.js'
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Session
23
+ // ---------------------------------------------------------------------------
24
+
25
+ /**
26
+ * The latest lifecycle state of a session, projected from the `session.status_*`
27
+ * events. `idle` = ready for the next user message; `running` = a turn is in
28
+ * flight; `terminated` = ended; `error` = the last turn failed.
29
+ */
30
+ export type SessionStatus = 'idle' | 'running' | 'terminated' | 'error'
31
+
32
+ /**
33
+ * The rolled-up token accounting for a session — the sum of every model request's
34
+ * usage plus the request count, projected from the `span.model_request_end` events
35
+ * and kept on the session row so listings/headers can show cost without replaying
36
+ * the log. `total_tokens` is the derived input+output sum.
37
+ *
38
+ * `model` is the model the usage is attributed to. Managed Agents reports no model
39
+ * id on its usage events, so it is seeded once at session create from the agent's
40
+ * configured model and preserved across recomputes (empty on sessions created
41
+ * before usage tracking existed).
42
+ */
43
+ export interface SessionUsage {
44
+ model: string
45
+ input_tokens: number
46
+ output_tokens: number
47
+ cache_read_input_tokens: number
48
+ cache_creation_input_tokens: number
49
+ total_tokens: number
50
+ request_count: number
51
+ }
52
+
53
+ /**
54
+ * One conversation with a configured Agent. Surrogate-keyed by `id` (a session is
55
+ * a graph node referenced by every event). `status` is denormalized from the
56
+ * event log for cheap listing; `usage` is the denormalized token total.
57
+ */
58
+ export interface Session extends AuditFields {
59
+ org_id: string
60
+ id: string
61
+ agent_key: string
62
+ title: string
63
+ status: SessionStatus
64
+ usage: SessionUsage
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Event types (the discriminant)
69
+ // ---------------------------------------------------------------------------
70
+
71
+ /**
72
+ * The discriminant of a {@link SessionEvent} — it selects which payload shape the
73
+ * event carries. v1 carries WHOLE events only; the delta/chunk variants
74
+ * (`agent.message.delta`, …) are intentionally absent.
75
+ */
76
+ export type EventType =
77
+ // User-originated (posted via the API or echoed back by the provider).
78
+ | 'user.message'
79
+ | 'user.interrupt'
80
+ | 'user.tool_confirmation'
81
+ | 'user.tool_result'
82
+ // Agent-originated.
83
+ | 'agent.message'
84
+ | 'agent.reasoning'
85
+ | 'agent.tool_use'
86
+ | 'agent.tool_result'
87
+ | 'agent.context_compacted'
88
+ | 'agent.artifacts'
89
+ // Session lifecycle.
90
+ | 'session.status_running'
91
+ | 'session.status_idle'
92
+ | 'session.status_terminated'
93
+ | 'session.error'
94
+ | 'session.updated'
95
+ // Span (observability).
96
+ | 'span.model_request_start'
97
+ | 'span.model_request_end'
98
+
99
+ /** The client-postable subset (everything else is produced by the runtime). */
100
+ export type ClientEventType =
101
+ | 'user.message'
102
+ | 'user.interrupt'
103
+ | 'user.tool_confirmation'
104
+ | 'user.tool_result'
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Content blocks (the self-tagged pieces of heterogeneous payloads)
108
+ // ---------------------------------------------------------------------------
109
+
110
+ /** A plain text block. Agent messages are text-only. */
111
+ export interface TextBlock {
112
+ type: 'text'
113
+ text: string
114
+ }
115
+
116
+ /**
117
+ * A file value object: a reference to a stored File (storage-service) plus light
118
+ * metadata. Embedded inline in a user-message {@link ContentBlock} (`type=file`)
119
+ * and nested under `image` in a tool {@link ResultBlock}.
120
+ */
121
+ export interface FileBlock {
122
+ file_id?: string
123
+ file_name?: string
124
+ mime_type?: string
125
+ size_in_bytes?: number
126
+ }
127
+
128
+ /**
129
+ * One block of a user/agent message body. For `type=text` only `text` is set; for
130
+ * `type=file` the {@link FileBlock} fields are promoted inline.
131
+ */
132
+ export interface ContentBlock extends FileBlock {
133
+ type: 'text' | 'file'
134
+ text?: string
135
+ }
136
+
137
+ /**
138
+ * One block of a tool result. Unlike message file blocks, an image nests its file
139
+ * under `image`; `json` carries an arbitrary raw object.
140
+ */
141
+ export interface ResultBlock {
142
+ type: 'text' | 'image' | 'json'
143
+ text?: string
144
+ image?: FileBlock
145
+ json?: unknown
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Event payloads (discriminated by the sibling event `type`)
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /** A user message body (text + file blocks). */
153
+ export interface UserMessagePayload {
154
+ content: ContentBlock[]
155
+ }
156
+
157
+ /** An agent message body (text blocks only). */
158
+ export interface AgentMessagePayload {
159
+ content: TextBlock[]
160
+ }
161
+
162
+ /**
163
+ * Files the agent produced in its sandbox this turn (reconciled from the session
164
+ * outputs), surfaced as downloadable attachments on the agent turn. A turn can
165
+ * produce several. Each {@link FileBlock} carries the storage-service `file_id`.
166
+ */
167
+ export interface AgentArtifactsPayload {
168
+ files: FileBlock[]
169
+ }
170
+
171
+ /**
172
+ * An agent reasoning ("thinking") block. v1 whole-events carry no text/signature
173
+ * from the provider, so both are typically empty.
174
+ */
175
+ export interface ReasoningPayload {
176
+ text: string
177
+ signature?: string
178
+ }
179
+
180
+ /**
181
+ * A tool invocation. `id` is the provider's tool_use event id — the correlation
182
+ * key a matching {@link ToolResultPayload}'s `tool_use_id` points back to.
183
+ * `tool_kind` is the execution location (action | mcp | client).
184
+ */
185
+ export interface ToolUsePayload {
186
+ id: string
187
+ name: string
188
+ input: unknown
189
+ tool_kind: ToolKind
190
+ }
191
+
192
+ /** The result of a tool invocation, linked to its use by `tool_use_id`. */
193
+ export interface ToolResultPayload {
194
+ tool_use_id: string
195
+ result: ResultBlock[]
196
+ /** "success" | "error" (open string for forward-compat). */
197
+ outcome: string
198
+ }
199
+
200
+ /** A user's decision on a tool that requested approval. */
201
+ export interface ToolConfirmationPayload {
202
+ tool_use_id: string
203
+ /** "approved" | "denied" (open string for forward-compat). */
204
+ decision: string
205
+ }
206
+
207
+ /** Announces that earlier context was summarized. */
208
+ export interface ContextCompactedPayload {
209
+ summary: string
210
+ }
211
+
212
+ /** A session-level error. */
213
+ export interface ErrorPayload {
214
+ message: string
215
+ code?: string
216
+ }
217
+
218
+ /**
219
+ * Why a turn went idle. `type` is an open string ("turn_ended" |
220
+ * "user_action_required" | …) so new provider reasons don't break decoding.
221
+ */
222
+ export interface StopReason {
223
+ type: string
224
+ }
225
+
226
+ /**
227
+ * Emitted when a turn ends. `event_ids` carries the tool_use event ids awaiting a
228
+ * client response when `stop_reason.type` is `user_action_required`.
229
+ */
230
+ export interface SessionIdlePayload {
231
+ stop_reason: StopReason
232
+ event_ids?: string[]
233
+ }
234
+
235
+ /**
236
+ * A server-side session metadata change — v1 carries the (often auto-generated)
237
+ * title. Projected onto {@link Session.title}; emitted only when the title changed.
238
+ */
239
+ export interface SessionUpdatedPayload {
240
+ title?: string
241
+ }
242
+
243
+ /** Opens a model-request span. */
244
+ export interface ModelRequestStartPayload {
245
+ model_id: string
246
+ }
247
+
248
+ /** Per-request token accounting. */
249
+ export interface ModelUsage {
250
+ input_tokens: number
251
+ output_tokens: number
252
+ cache_read_input_tokens?: number
253
+ cache_creation_input_tokens?: number
254
+ }
255
+
256
+ /** Closes a model-request span with usage. */
257
+ export interface ModelRequestEndPayload {
258
+ model_usage: ModelUsage
259
+ }
260
+
261
+ /**
262
+ * The body of events that carry no fields (`user.interrupt`,
263
+ * `session.status_running`, `session.status_terminated`).
264
+ */
265
+ export type EmptyPayload = Record<string, never>
266
+
267
+ // ---------------------------------------------------------------------------
268
+ // SessionEvent (the append-only log envelope)
269
+ // ---------------------------------------------------------------------------
270
+
271
+ /** Shared envelope fields carried by every {@link SessionEvent}. */
272
+ export interface SessionEventEnvelope {
273
+ /** Dedupe key: our uuid for user events, the provider's id for agent events. */
274
+ id: string
275
+ session_id: string
276
+ org_id: string
277
+ /** Groups events into user/agent turns. */
278
+ turn_id: string
279
+ /** Per-session monotonic cursor (the SSE frame `id`). */
280
+ seq: number
281
+ created_by: UserRef
282
+ /** Meaningful only for user events: null = queued, timestamp = consumed. */
283
+ processed_at: string | null
284
+ created_at: string
285
+ }
286
+
287
+ /**
288
+ * One entry of the append-only session log — discriminated by `type`. The same
289
+ * envelope is delivered byte-for-byte over both the durable (REST) and live (SSE)
290
+ * channels, keyed by the same `seq`, so the two merge into one ordered log.
291
+ */
292
+ export type SessionEvent = SessionEventEnvelope &
293
+ (
294
+ | { type: 'user.message'; payload: UserMessagePayload }
295
+ | { type: 'user.interrupt'; payload: EmptyPayload }
296
+ | { type: 'user.tool_confirmation'; payload: ToolConfirmationPayload }
297
+ | { type: 'user.tool_result'; payload: ToolResultPayload }
298
+ | { type: 'agent.message'; payload: AgentMessagePayload }
299
+ | { type: 'agent.reasoning'; payload: ReasoningPayload }
300
+ | { type: 'agent.tool_use'; payload: ToolUsePayload }
301
+ | { type: 'agent.tool_result'; payload: ToolResultPayload }
302
+ | { type: 'agent.context_compacted'; payload: ContextCompactedPayload }
303
+ | { type: 'agent.artifacts'; payload: AgentArtifactsPayload }
304
+ | { type: 'session.status_running'; payload: EmptyPayload }
305
+ | { type: 'session.status_idle'; payload: SessionIdlePayload }
306
+ | { type: 'session.status_terminated'; payload: EmptyPayload }
307
+ | { type: 'session.error'; payload: ErrorPayload }
308
+ | { type: 'session.updated'; payload: SessionUpdatedPayload }
309
+ | { type: 'span.model_request_start'; payload: ModelRequestStartPayload }
310
+ | { type: 'span.model_request_end'; payload: ModelRequestEndPayload }
311
+ )
312
+ // A closed discriminated union (the v1 event set) so consumers narrow `payload`
313
+ // by `type`. An unrecognized future event type still parses at runtime (it is cast
314
+ // from JSON) — a switch over `type` simply won't match it, which is the intended
315
+ // skip-and-ignore behavior.
316
+
317
+ /**
318
+ * The `GroupByTurn` projection — NEVER stored, recomputed from the event log.
319
+ * `events` hold the envelopes verbatim, grouped by `turn_id` and ordered by `seq`.
320
+ * The web folds this client-side from the live event stream; this type mirrors the
321
+ * server's `GET /sessions/:id/turns` for completeness.
322
+ */
323
+ export interface Turn {
324
+ id: string
325
+ role: 'user' | 'agent'
326
+ events: SessionEvent[]
327
+ }
328
+
329
+ // ---------------------------------------------------------------------------
330
+ // Requests / query options
331
+ // ---------------------------------------------------------------------------
332
+
333
+ /**
334
+ * A client tool the launching surface advertises when it opens a session: a custom
335
+ * tool the agent calls but the browser renders/executes, returning the result. Only
336
+ * these three fields cross the wire — the render slot and React component are
337
+ * frontend-only. `input_schema` is a JSON Schema (draft-07) object.
338
+ */
339
+ export interface ClientToolSchema {
340
+ name: string
341
+ description: string
342
+ input_schema: Record<string, unknown>
343
+ }
344
+
345
+ /**
346
+ * Opens a session against a configured agent. `title` is optional. `client_tools` is
347
+ * the launching surface's client-tool catalog, attached to this session only — a
348
+ * surface that can't render them simply omits the field, so the agent never gets a
349
+ * tool nobody can answer.
350
+ */
351
+ export interface CreateSessionRequest {
352
+ agent_key: string
353
+ title?: string
354
+ client_tools?: ClientToolSchema[]
355
+ }
356
+
357
+ /**
358
+ * Posts a client event onto a session. Only client-postable `user.*` types are
359
+ * accepted; `payload` is the matching payload for the `type`.
360
+ */
361
+ export interface AppendEventRequest {
362
+ type: ClientEventType
363
+ /**
364
+ * Optionally supply the turn's id (a UUID) so the client can render the message
365
+ * optimistically under a stable identity that matches the event echoed back
366
+ * over the stream. Omitted, the service mints one.
367
+ */
368
+ turn_id?: string
369
+ payload?: unknown
370
+ }
371
+
372
+ /** List filter for sessions (in addition to pagination/sorting). */
373
+ export interface ListSessionsOptions extends ListOptions {
374
+ /** Filter by the agent the session runs. */
375
+ agent_key?: string
376
+ }
377
+
378
+ /**
379
+ * Forward cursor over the durable event log. `after_seq` is the exclusive lower
380
+ * bound (the last-seen seq); `order` defaults to asc (chronological). `types`
381
+ * restricts the result to specific event types (e.g. `['agent.message']`);
382
+ * omitted, every type is returned.
383
+ */
384
+ export interface SessionEventsOptions {
385
+ after_seq?: number
386
+ limit?: number
387
+ order?: 'asc' | 'desc'
388
+ types?: EventType[]
389
+ }
390
+
391
+ /** Options for opening the live SSE stream. */
392
+ export interface SessionStreamOptions {
393
+ /** Replay from this seq (exclusive) before following live. */
394
+ afterSeq?: number
395
+ /** Abort to tear the stream down. */
396
+ signal?: AbortSignal
397
+ }
@@ -0,0 +1,197 @@
1
+ import type { ProteosClient } from '../client.js'
2
+ import { PageIterator } from '../iterator.js'
3
+ import type { ListResult } from '../types/common.js'
4
+ import type {
5
+ AppendEventRequest,
6
+ ContentBlock,
7
+ CreateSessionRequest,
8
+ FileBlock,
9
+ ListSessionsOptions,
10
+ ResultBlock,
11
+ Session,
12
+ SessionEvent,
13
+ SessionEventsOptions,
14
+ SessionStreamOptions,
15
+ Turn,
16
+ } from './session-types.js'
17
+
18
+ const SESSIONS_BASE_PATH = '/agents/v1/sessions'
19
+
20
+ /**
21
+ * Service for managing agent Sessions — a conversation with a configured Agent,
22
+ * backed by an append-only event log. Covers the full agent-service session
23
+ * surface: CRUD, the durable event history, posting client events, the derived
24
+ * turns projection, and the live SSE stream.
25
+ */
26
+ export interface SessionService {
27
+ /** Lists sessions, auto-paginating. */
28
+ list(options?: ListSessionsOptions): PageIterator<Session, ListSessionsOptions>
29
+ /** Fetches a single page of sessions with pagination metadata. */
30
+ listPage(options?: ListSessionsOptions): Promise<ListResult<Session>>
31
+ /** Gets a single session by id. @throws {ProteosError} 404 if not found. */
32
+ get(id: string): Promise<Session>
33
+ /** Opens a new session against a configured agent. @throws {ProteosError} 400. */
34
+ create(request: CreateSessionRequest): Promise<Session>
35
+
36
+ /**
37
+ * Reads a page of the durable event log (Postgres), a forward `after_seq`
38
+ * cursor ordered ascending by default. The live channel is {@link stream}.
39
+ */
40
+ events(id: string, options?: SessionEventsOptions): Promise<SessionEvent[]>
41
+ /** Posts a client (`user.*`) event onto a session. @throws {ProteosError} 400/409. */
42
+ appendEvent(id: string, request: AppendEventRequest): Promise<SessionEvent>
43
+ /** The server-side `GroupByTurn` projection of the full event log. */
44
+ turns(id: string): Promise<Turn[]>
45
+
46
+ /**
47
+ * Live-tails the session's event stream over SSE, yielding each
48
+ * {@link SessionEvent} as it arrives. Replays from `afterSeq` (exclusive) then
49
+ * follows live; reconnects resume gap-free. Runs until the `AbortSignal` fires.
50
+ */
51
+ stream(id: string, options?: SessionStreamOptions): AsyncGenerator<SessionEvent, void, unknown>
52
+
53
+ /**
54
+ * Convenience: appends a `user.message` with a single text block. Pass
55
+ * `turnId` (a UUID) to supply the turn's id so the caller can render the
56
+ * message optimistically under an id matching the echoed event.
57
+ */
58
+ sendMessage(id: string, text: string, turnId?: string): Promise<SessionEvent>
59
+ /**
60
+ * Convenience: appends a `user.message` built from optional text and file
61
+ * attachments. Each {@link FileBlock} carries the storage-service `file_id`; the
62
+ * backend uploads it to the model provider and mounts it into the agent's sandbox
63
+ * at send time. Pass `turnId` (a UUID) for optimistic rendering. At least one of
64
+ * `text` / `files` should be present.
65
+ */
66
+ sendUserMessage(
67
+ id: string,
68
+ input: { text?: string; files?: FileBlock[]; turnId?: string },
69
+ ): Promise<SessionEvent>
70
+ /** Convenience: appends a `user.interrupt` to halt the running turn. */
71
+ interrupt(id: string): Promise<SessionEvent>
72
+ /**
73
+ * Convenience: appends a `user.tool_result` answering a client tool the agent
74
+ * called (an `agent.tool_use` with `tool_kind: 'client'`). `toolUseId` is that
75
+ * tool-use event's id (carried in the idle event's `event_ids`); the backend
76
+ * relays the result to Anthropic as a `user.custom_tool_result` to resume the turn.
77
+ * `outcome` defaults to `'success'`.
78
+ */
79
+ submitToolResult(
80
+ id: string,
81
+ input: { toolUseId: string; result: ResultBlock[]; outcome?: 'success' | 'error' },
82
+ ): Promise<SessionEvent>
83
+ }
84
+
85
+ export class SessionServiceImpl implements SessionService {
86
+ constructor(private readonly client: ProteosClient) {}
87
+
88
+ list(options: ListSessionsOptions = {}): PageIterator<Session, ListSessionsOptions> {
89
+ return new PageIterator((opts) => this.listPage(opts), options)
90
+ }
91
+
92
+ async listPage(options: ListSessionsOptions = {}): Promise<ListResult<Session>> {
93
+ return this.client.requestWithQuery<ListResult<Session>>('GET', SESSIONS_BASE_PATH, options)
94
+ }
95
+
96
+ async get(id: string): Promise<Session> {
97
+ return this.client.request<Session>('GET', `${SESSIONS_BASE_PATH}/${id}`)
98
+ }
99
+
100
+ async create(request: CreateSessionRequest): Promise<Session> {
101
+ return this.client.request<Session>('POST', SESSIONS_BASE_PATH, request)
102
+ }
103
+
104
+ async events(id: string, options: SessionEventsOptions = {}): Promise<SessionEvent[]> {
105
+ // The backend reads `types` as a single comma-separated query param (a string
106
+ // it splits into a `type IN (...)` filter), so join the array rather than
107
+ // letting the serializer emit repeated `types=` params.
108
+ const { types, ...rest } = options
109
+ const query = {
110
+ ...rest,
111
+ ...(types && types.length > 0 ? { types: types.join(',') } : {}),
112
+ }
113
+ const response = await this.client.requestWithQuery<{ data: SessionEvent[] }>(
114
+ 'GET',
115
+ `${SESSIONS_BASE_PATH}/${id}/events`,
116
+ query,
117
+ )
118
+ return response.data ?? []
119
+ }
120
+
121
+ async appendEvent(id: string, request: AppendEventRequest): Promise<SessionEvent> {
122
+ return this.client.request<SessionEvent>('POST', `${SESSIONS_BASE_PATH}/${id}/events`, request)
123
+ }
124
+
125
+ async turns(id: string): Promise<Turn[]> {
126
+ const response = await this.client.request<{ data: Turn[] }>(
127
+ 'GET',
128
+ `${SESSIONS_BASE_PATH}/${id}/turns`,
129
+ )
130
+ return response.data ?? []
131
+ }
132
+
133
+ async *stream(
134
+ id: string,
135
+ options: SessionStreamOptions = {},
136
+ ): AsyncGenerator<SessionEvent, void, unknown> {
137
+ const query = options.afterSeq ? `?after_seq=${options.afterSeq}` : ''
138
+ const path = `${SESSIONS_BASE_PATH}/${id}/events/stream${query}`
139
+ const streamOptions = options.signal ? { signal: options.signal } : {}
140
+ for await (const frame of this.client.streamEvents(path, streamOptions)) {
141
+ const event = parseEvent(frame.data)
142
+ if (event) yield event
143
+ }
144
+ }
145
+
146
+ async sendMessage(id: string, text: string, turnId?: string): Promise<SessionEvent> {
147
+ return this.sendUserMessage(id, { text, ...(turnId ? { turnId } : {}) })
148
+ }
149
+
150
+ async sendUserMessage(
151
+ id: string,
152
+ input: { text?: string; files?: FileBlock[]; turnId?: string },
153
+ ): Promise<SessionEvent> {
154
+ const content: ContentBlock[] = []
155
+ const text = input.text?.trim()
156
+ if (text) content.push({ type: 'text', text })
157
+ for (const file of input.files ?? []) {
158
+ content.push({ type: 'file', ...file })
159
+ }
160
+ return this.appendEvent(id, {
161
+ type: 'user.message',
162
+ ...(input.turnId ? { turn_id: input.turnId } : {}),
163
+ payload: { content },
164
+ })
165
+ }
166
+
167
+ async interrupt(id: string): Promise<SessionEvent> {
168
+ return this.appendEvent(id, { type: 'user.interrupt' })
169
+ }
170
+
171
+ async submitToolResult(
172
+ id: string,
173
+ input: { toolUseId: string; result: ResultBlock[]; outcome?: 'success' | 'error' },
174
+ ): Promise<SessionEvent> {
175
+ return this.appendEvent(id, {
176
+ type: 'user.tool_result',
177
+ payload: {
178
+ tool_use_id: input.toolUseId,
179
+ result: input.result,
180
+ outcome: input.outcome ?? 'success',
181
+ },
182
+ })
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Parses one SSE frame's `data` into a SessionEvent, returning null for an empty
188
+ * or unparseable frame — a single bad frame is skipped, not fatal to the stream.
189
+ */
190
+ function parseEvent(data: string): SessionEvent | null {
191
+ if (!data) return null
192
+ try {
193
+ return JSON.parse(data) as SessionEvent
194
+ } catch {
195
+ return null
196
+ }
197
+ }
@@ -0,0 +1,89 @@
1
+ import type { ProteosClient } from '../client.js'
2
+ import { PageIterator } from '../iterator.js'
3
+ import type { ListResult } from '../types/common.js'
4
+ import type { ListSkillsOptions, Skill, SkillVersion } from './types.js'
5
+
6
+ const SKILLS_BASE_PATH = '/agents/v1/skills'
7
+
8
+ /**
9
+ * Service for managing Skills — versioned bundles of instructions + files in the
10
+ * Anthropic Agent-Skills shape. Skills are created and updated only via
11
+ * {@link SkillService.deploy} (a multipart bundle upload); there is no JSON
12
+ * create/update.
13
+ */
14
+ export interface SkillService {
15
+ /** Lists skills, auto-paginating. */
16
+ list(options?: ListSkillsOptions): PageIterator<Skill, ListSkillsOptions>
17
+ /** Fetches a single page of skills with pagination metadata. */
18
+ listPage(options?: ListSkillsOptions): Promise<ListResult<Skill>>
19
+ /** Gets a skill (with its current version + bundle pin) by key. @throws {ProteosError} 404. */
20
+ get(key: string): Promise<Skill>
21
+ /**
22
+ * Deploys a skill bundle (a `.tar.gz` containing `SKILL.md` at the root plus
23
+ * supporting files). The skill's key — its canonical identifier — is read
24
+ * from the bundle's `SKILL.md` frontmatter `name`, so no key is supplied here.
25
+ * Creates the skill on first deploy and a new immutable version each time.
26
+ *
27
+ * @param bundle - The `.tar.gz` bundle bytes.
28
+ * @param options.displayName - Optional human-readable display name.
29
+ * @param options.moduleSlug - Optional owning module slug (tags the skill so it
30
+ * can be filtered + round-tripped by module). Empty for a standalone deploy.
31
+ */
32
+ deploy(bundle: Blob | File, options?: { displayName?: string; moduleSlug?: string }): Promise<Skill>
33
+ /** Deletes a skill and all its versions. @throws {ProteosError} 404. */
34
+ delete(key: string): Promise<void>
35
+ /** Lists all immutable versions of a skill, newest first. */
36
+ listVersions(key: string): Promise<SkillVersion[]>
37
+ /** Gets a specific immutable version by number. @throws {ProteosError} 404. */
38
+ getVersion(key: string, number: number): Promise<SkillVersion>
39
+ }
40
+
41
+ export class SkillServiceImpl implements SkillService {
42
+ constructor(private readonly client: ProteosClient) {}
43
+
44
+ list(options: ListSkillsOptions = {}): PageIterator<Skill, ListSkillsOptions> {
45
+ return new PageIterator((opts) => this.listPage(opts), options)
46
+ }
47
+
48
+ async listPage(options: ListSkillsOptions = {}): Promise<ListResult<Skill>> {
49
+ return this.client.requestWithQuery<ListResult<Skill>>('GET', SKILLS_BASE_PATH, options)
50
+ }
51
+
52
+ async get(key: string): Promise<Skill> {
53
+ return this.client.request<Skill>('GET', `${SKILLS_BASE_PATH}/${key}`)
54
+ }
55
+
56
+ async deploy(
57
+ bundle: Blob | File,
58
+ options?: { displayName?: string; moduleSlug?: string },
59
+ ): Promise<Skill> {
60
+ const formData = new FormData()
61
+ const filename = bundle instanceof File ? bundle.name : 'skill.tar.gz'
62
+ formData.append('bundle', bundle, filename)
63
+ if (options?.displayName) {
64
+ formData.append('display_name', options.displayName)
65
+ }
66
+ if (options?.moduleSlug) {
67
+ formData.append('module_slug', options.moduleSlug)
68
+ }
69
+ // Returns a bare Skill (no `{ data }` envelope). The key is derived from the
70
+ // bundle's SKILL.md frontmatter server-side.
71
+ return this.client.requestMultipart<Skill>('POST', `${SKILLS_BASE_PATH}/deploy`, formData)
72
+ }
73
+
74
+ async delete(key: string): Promise<void> {
75
+ await this.client.request<void>('DELETE', `${SKILLS_BASE_PATH}/${key}`)
76
+ }
77
+
78
+ async listVersions(key: string): Promise<SkillVersion[]> {
79
+ const response = await this.client.request<{ data: SkillVersion[] }>(
80
+ 'GET',
81
+ `${SKILLS_BASE_PATH}/${key}/versions`,
82
+ )
83
+ return response.data
84
+ }
85
+
86
+ async getVersion(key: string, number: number): Promise<SkillVersion> {
87
+ return this.client.request<SkillVersion>('GET', `${SKILLS_BASE_PATH}/${key}/versions/${number}`)
88
+ }
89
+ }