@runmark/sdk 0.1.0 → 0.1.3
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 +19 -0
- package/README.md +365 -125
- package/dist/client.d.ts +48 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +110 -4
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -1
- package/dist/http.d.ts +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +70 -5
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/routes.d.ts +25 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +54 -2
- package/dist/routes.js.map +1 -1
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +10 -1
- package/dist/sse.js.map +1 -1
- package/dist/types.d.ts +314 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webhooks.d.ts +6 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +73 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +11 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.3
|
|
6
|
+
|
|
7
|
+
- Add typed repository runtime webhook management, evidence pagination, replay, rotation status,
|
|
8
|
+
run/HITL fetch helpers, and a Standard Webhooks-compatible signature verifier.
|
|
9
|
+
- Add immutable `dev`/`main` endpoint lanes, strict all/selected-agent scopes, disabled-first test
|
|
10
|
+
setup, repository-wide delivery search/detail APIs, deleted-endpoint history, and claimed attempt
|
|
11
|
+
URL/timestamp/secret-revision/next-retry evidence.
|
|
12
|
+
|
|
13
|
+
## 0.1.2
|
|
14
|
+
|
|
15
|
+
- Add request-ID submission and lookup, durable cancellation, HITL request
|
|
16
|
+
correlation, active-organization forwarding, and replay-cursor contracts.
|
|
17
|
+
- Add durable response/error types and preserve typed API error codes.
|
|
18
|
+
- Validate and bound JSON/SSE responses, including fragmented CRLF events,
|
|
19
|
+
malformed envelopes, oversized bodies/events, and reader cancellation.
|
package/README.md
CHANGED
|
@@ -1,81 +1,273 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @runmark/sdk
|
|
2
|
+
|
|
3
|
+
Runtime lifecycle webhooks use Standard Webhooks signatures and at-least-once delivery. Use
|
|
4
|
+
`verifyWebhookSignature` against the exact raw request bytes before parsing JSON, reject timestamps
|
|
5
|
+
outside the default five-minute window, and deduplicate on the immutable event `id`. Delivery order
|
|
6
|
+
may differ from the run-local `sequence`; use the repository-bound `cursor` for replay ranges.
|
|
7
|
+
|
|
8
|
+
Webhook payloads are intentionally minimized. Follow the version-pinned run and human-action URLs
|
|
9
|
+
with an authorized repository token when full evidence is required. Endpoint secrets are shown only
|
|
10
|
+
once when created or rotated, and overlapping revisions remain valid for 24 hours.
|
|
11
|
+
|
|
12
|
+
Endpoints are created disabled and bind immutably to the exact lowercase `dev` or `main` lane.
|
|
13
|
+
Configure an explicit `agent_scope`, send a targeted test while disabled, then enable the endpoint.
|
|
14
|
+
Use `skyforge.webhooks.listRepositoryDeliveries(...)` for repository-wide server-side search and
|
|
15
|
+
cursor pagination, and `skyforge.webhooks.getDelivery(...)` to retrieve the exact persisted body and
|
|
16
|
+
claimed URL/timestamp/revision evidence for every retained attempt. Endpoint edits and rotations
|
|
17
|
+
affect later claims without rewriting earlier attempt evidence.
|
|
18
|
+
|
|
19
|
+
Server-side TypeScript SDK for calling Skyforge agent chat APIs from trusted
|
|
20
|
+
backend code.
|
|
21
|
+
|
|
22
|
+
Use this package from Express apps, Next.js route handlers, serverless
|
|
23
|
+
functions, jobs, and backend proxies. Do not use it in browser code.
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
Browser
|
|
27
|
+
-> your backend
|
|
28
|
+
-> @runmark/sdk
|
|
29
|
+
-> Skyforge API
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use `@runmark/agent-chat` when you need the browser or React chat UI.
|
|
33
|
+
|
|
34
|
+
## When To Use It
|
|
35
|
+
|
|
36
|
+
- Send messages to a configured Skyforge agent branch from your backend.
|
|
37
|
+
- Build an app proxy for `@runmark/agent-chat`.
|
|
38
|
+
- Resume a known Skyforge chat by `chatId`.
|
|
39
|
+
- Attach optional host-app mapping metadata with `externalId`.
|
|
40
|
+
- Build server-side chat recovery, search, or history using agent-scoped chat
|
|
41
|
+
lookup.
|
|
42
|
+
- Upload, list, and delete files attached to a chat.
|
|
43
|
+
- Stream durable run events and submit human actions.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @runmark/sdk
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Node.js `24.17.0` or newer
|
|
54
|
+
- A server-side Skyforge API token
|
|
55
|
+
- Native `fetch`, `FormData`, `ReadableStream`, and `AbortSignal`
|
|
56
|
+
|
|
57
|
+
## Client Setup
|
|
2
58
|
|
|
3
|
-
|
|
59
|
+
```ts
|
|
60
|
+
import { SkyforgeClient } from "@runmark/sdk";
|
|
61
|
+
|
|
62
|
+
const skyforge = new SkyforgeClient({
|
|
63
|
+
apiKey: process.env.SKYFORGE_API_TOKEN!,
|
|
64
|
+
baseUrl: process.env.SKYFORGE_API_BASE_URL ?? "https://api.skyforge.ai",
|
|
65
|
+
});
|
|
66
|
+
```
|
|
4
67
|
|
|
5
|
-
|
|
6
|
-
is intentionally retained until a release process, package metadata, and npm
|
|
7
|
-
publishing policy are approved.
|
|
68
|
+
Common server-side environment variables:
|
|
8
69
|
|
|
9
|
-
|
|
70
|
+
```bash
|
|
71
|
+
SKYFORGE_API_BASE_URL=https://api.skyforge.ai
|
|
72
|
+
SKYFORGE_API_TOKEN=
|
|
73
|
+
SKYFORGE_AGENT_ID=
|
|
74
|
+
SKYFORGE_AGENT_BRANCH_ID=
|
|
75
|
+
```
|
|
10
76
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- Zero runtime dependencies.
|
|
14
|
-
- Uses native `fetch`, `FormData`, `ReadableStream`, and `AbortSignal`.
|
|
77
|
+
`SKYFORGE_API_TOKEN` must stay on the server. Never bundle it into frontend
|
|
78
|
+
code.
|
|
15
79
|
|
|
16
|
-
##
|
|
80
|
+
## Auth And Security Model
|
|
17
81
|
|
|
18
|
-
|
|
82
|
+
The SDK sends:
|
|
19
83
|
|
|
20
84
|
```http
|
|
21
|
-
Authorization: Bearer <
|
|
85
|
+
Authorization: Bearer <SKYFORGE_API_TOKEN>
|
|
22
86
|
```
|
|
23
87
|
|
|
24
|
-
|
|
25
|
-
|
|
88
|
+
It does not require browser cookies, CSRF tokens, browser credentials, or
|
|
89
|
+
frontend auth routes.
|
|
90
|
+
|
|
91
|
+
Use your own app authentication and tenancy checks before deciding which
|
|
92
|
+
Skyforge agent, branch, chat, or `externalId` a request may use. `externalId`
|
|
93
|
+
is metadata for mapping chats back to your app; it is not authentication and it
|
|
94
|
+
does not bypass Skyforge API-token authorization.
|
|
95
|
+
|
|
96
|
+
## Send a Message
|
|
26
97
|
|
|
27
|
-
|
|
98
|
+
```ts
|
|
99
|
+
const response = await skyforge.agentBranches.sendMessage({
|
|
100
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
101
|
+
message: "Help me plan this outdoor event.",
|
|
102
|
+
externalId: "event_123",
|
|
103
|
+
requestId: crypto.randomUUID(),
|
|
104
|
+
});
|
|
28
105
|
|
|
29
|
-
|
|
30
|
-
|
|
106
|
+
console.log(response.request_id, response.agent_run_id, response.chat_id, response.terminal);
|
|
107
|
+
```
|
|
31
108
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
109
|
+
`externalId` is optional host-app mapping metadata. Use it for values your app
|
|
110
|
+
already understands, such as an event ID, support ticket ID, session ID,
|
|
111
|
+
project ID, or case ID.
|
|
35
112
|
|
|
36
|
-
|
|
113
|
+
When continuing an existing Skyforge chat, pass `chatId`:
|
|
37
114
|
|
|
38
115
|
```ts
|
|
39
|
-
|
|
116
|
+
await skyforge.agentBranches.sendMessage({
|
|
117
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
118
|
+
chatId: "skyforge-chat-id",
|
|
119
|
+
externalId: "event_123",
|
|
120
|
+
message: "Summarize the current event status.",
|
|
121
|
+
structuredContext: {
|
|
122
|
+
weather: "windy",
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
40
126
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
127
|
+
Skyforge may reject a request that tries to continue an existing `chatId` with a
|
|
128
|
+
different `externalId` than the chat already has.
|
|
129
|
+
|
|
130
|
+
Response fields mirror the Skyforge API payloads and use API field names such
|
|
131
|
+
as `chat_id`, `external_id`, `agent_branch_id`, and `pending_human_actions`.
|
|
132
|
+
|
|
133
|
+
Every turn is a durable worker-owned run, regardless of the branch's legacy
|
|
134
|
+
runtime mode. `terminal=false` means the HTTP delivery returned before
|
|
135
|
+
execution resolved; preserve `request_id`, `agent_run_id`, and `chat_id` for
|
|
136
|
+
lookup, event replay, and explicit cancellation. Delivery timeout or abort does
|
|
137
|
+
not fail or cancel the run.
|
|
138
|
+
|
|
139
|
+
## Request Recovery And Cancellation
|
|
140
|
+
|
|
141
|
+
Use one request ID for one immutable submission contract. An exact retry can
|
|
142
|
+
resolve to the existing run; changing delivery mode or semantic inputs returns
|
|
143
|
+
generic `409 request_id_conflict`.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const lookup = await skyforge.agentBranches.lookupRequest({
|
|
147
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
148
|
+
requestId,
|
|
44
149
|
});
|
|
150
|
+
|
|
151
|
+
if (!lookup.terminal) {
|
|
152
|
+
await skyforge.agentRuns.cancel({
|
|
153
|
+
agentRunId: lookup.agent_run_id,
|
|
154
|
+
reason: "User requested cancellation",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
45
157
|
```
|
|
46
158
|
|
|
47
|
-
|
|
159
|
+
Cancellation is cooperative. It prevents new provider/tool calls after the
|
|
160
|
+
worker observes the request, but cannot undo an already transmitted external
|
|
161
|
+
side effect. Do not translate client disconnect or an SDK `AbortSignal` into a
|
|
162
|
+
cancellation request.
|
|
163
|
+
|
|
164
|
+
## Recover Chats By External ID
|
|
165
|
+
|
|
166
|
+
Use `skyforge.chats.list(...)` from your backend to recover chats for an agent.
|
|
167
|
+
The lookup is agent-scoped and can be narrowed to one branch.
|
|
48
168
|
|
|
49
169
|
```ts
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
170
|
+
const { chats, nextCursor } = await skyforge.chats.list({
|
|
171
|
+
agentId: process.env.SKYFORGE_AGENT_ID!,
|
|
172
|
+
externalId: "event_123",
|
|
53
173
|
});
|
|
54
174
|
|
|
55
|
-
|
|
175
|
+
for (const chat of chats) {
|
|
176
|
+
console.log(chat.chat_id, chat.external_id, chat.agent_branch_name);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(nextCursor);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The SDK calls:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
GET /api/skyforge-chat/agents/{agent_id}/chats
|
|
56
186
|
```
|
|
57
187
|
|
|
58
|
-
|
|
188
|
+
Supported query params are `agent_branch_id`, `chat_id`, `external_id`,
|
|
189
|
+
`limit`, and `cursor`. The raw API response field is `next_cursor`; the SDK
|
|
190
|
+
normalizes it to `nextCursor`.
|
|
191
|
+
|
|
192
|
+
List items include:
|
|
193
|
+
|
|
194
|
+
- `agent_id`
|
|
195
|
+
- `agent_branch_id`
|
|
196
|
+
- `agent_branch_name`
|
|
197
|
+
- `chat_id`
|
|
198
|
+
- `external_id`
|
|
199
|
+
- `chat_title`
|
|
200
|
+
- `created_at`
|
|
201
|
+
- `updated_at`
|
|
202
|
+
|
|
203
|
+
## Chat Lookup Examples
|
|
204
|
+
|
|
205
|
+
Find one known chat:
|
|
59
206
|
|
|
60
207
|
```ts
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
chatId: "
|
|
64
|
-
message: "Summarize the latest run.",
|
|
65
|
-
structuredContext: {
|
|
66
|
-
priority: "high",
|
|
67
|
-
},
|
|
208
|
+
const { chats } = await skyforge.chats.list({
|
|
209
|
+
agentId: process.env.SKYFORGE_AGENT_ID!,
|
|
210
|
+
chatId: "skyforge-chat-id",
|
|
68
211
|
});
|
|
69
212
|
|
|
70
|
-
|
|
213
|
+
const chat = chats[0];
|
|
71
214
|
```
|
|
72
215
|
|
|
73
|
-
|
|
216
|
+
Narrow lookup to one branch:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
const page = await skyforge.chats.list({
|
|
220
|
+
agentId: process.env.SKYFORGE_AGENT_ID!,
|
|
221
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
222
|
+
externalId: "event_123",
|
|
223
|
+
limit: 20,
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Paginate through results:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
let cursor: string | undefined;
|
|
231
|
+
|
|
232
|
+
do {
|
|
233
|
+
const page = await skyforge.chats.list({
|
|
234
|
+
agentId: process.env.SKYFORGE_AGENT_ID!,
|
|
235
|
+
externalId: "event_123",
|
|
236
|
+
cursor,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
for (const chat of page.chats) {
|
|
240
|
+
// Render or store the chat mapping in your app.
|
|
241
|
+
console.log(chat.chat_id, chat.updated_at);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
cursor = page.nextCursor ?? undefined;
|
|
245
|
+
} while (cursor);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Build a SaaS Chat History
|
|
249
|
+
|
|
250
|
+
Use `externalId` when your app already has a record that should own the
|
|
251
|
+
conversation, such as an event, ticket, project, or session. Your backend can
|
|
252
|
+
later call `skyforge.chats.list({ agentId, externalId })` to build a chat
|
|
253
|
+
history or resume-chat experience in your own product.
|
|
254
|
+
|
|
255
|
+
Skyforge provides the lookup primitive. Your SaaS app still owns the end-user
|
|
256
|
+
history UI, app-level permissions, and the decision about which `externalId` is
|
|
257
|
+
valid for the current user. Treat browser-provided external IDs as metadata,
|
|
258
|
+
not trusted identity.
|
|
259
|
+
|
|
260
|
+
The public [`Skyforge-AI/api-starter`](https://github.com/Skyforge-AI/api-starter)
|
|
261
|
+
uses `@runmark/sdk` on the backend and `@runmark/agent-chat` in the browser. To
|
|
262
|
+
add a chat-history UI to your own app, call `skyforge.chats.list(...)` from
|
|
263
|
+
your backend and pass the selected `chatId` to the chat component.
|
|
264
|
+
|
|
265
|
+
## Stream Run Events
|
|
74
266
|
|
|
75
267
|
```ts
|
|
76
268
|
const events = await skyforge.agentRuns.streamEvents({
|
|
77
|
-
agentRunId: "
|
|
78
|
-
afterSequence:
|
|
269
|
+
agentRunId: "agent-run-id",
|
|
270
|
+
afterSequence: 0,
|
|
79
271
|
});
|
|
80
272
|
|
|
81
273
|
for await (const event of events) {
|
|
@@ -83,26 +275,66 @@ for await (const event of events) {
|
|
|
83
275
|
}
|
|
84
276
|
```
|
|
85
277
|
|
|
86
|
-
Breaking out of the loop cancels the underlying
|
|
278
|
+
Breaking out of the loop cancels the underlying stream reader.
|
|
87
279
|
|
|
88
|
-
|
|
280
|
+
`afterSequence` is an explicit persisted replay cursor. The low-level SDK does
|
|
281
|
+
not run an infinite reconnect loop or deduplicate events for you; reconnect
|
|
282
|
+
with the last applied sequence and ignore duplicate sequence values in the
|
|
283
|
+
consumer. The parser supports CRLF/chunk boundaries and multiline data, rejects
|
|
284
|
+
malformed or oversized events, and always releases its reader.
|
|
285
|
+
|
|
286
|
+
## Source References
|
|
287
|
+
|
|
288
|
+
When Advanced RAG injects labeled sources into an answer, chat messages may
|
|
289
|
+
include `source_references`. These references are safe public metadata for the
|
|
290
|
+
labels visible in the assistant text, such as `[S1]`; they are not the hidden
|
|
291
|
+
RAG prompt or full source documents.
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
const reply = await skyforge.agentBranches.sendMessage({
|
|
295
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
296
|
+
message: "What does Advanced RAG change?",
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
for (const message of reply.messages) {
|
|
300
|
+
for (const source of message.source_references ?? []) {
|
|
301
|
+
console.log(source.display_label, source.snippet, source.source_type);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
For run-based integrations, source references can also be loaded after the run
|
|
307
|
+
completes:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const { source_references } = await skyforge.agentRuns.getSourceReferences({
|
|
311
|
+
agentRunId: "agent-run-id",
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Match references by exact `source_label` or `display_label`, never by array
|
|
316
|
+
position. Source links are not part of the MVP source-reference contract.
|
|
317
|
+
|
|
318
|
+
## Submit a Human Action
|
|
89
319
|
|
|
90
320
|
```ts
|
|
91
321
|
await skyforge.humanActions.submit({
|
|
92
|
-
humanActionRequestId: "
|
|
322
|
+
humanActionRequestId: "human-action-request-id",
|
|
323
|
+
requestId: crypto.randomUUID(),
|
|
324
|
+
activeOrganizationId: "organization-id",
|
|
93
325
|
values: {
|
|
94
326
|
approved: true,
|
|
95
327
|
},
|
|
96
328
|
});
|
|
97
329
|
```
|
|
98
330
|
|
|
99
|
-
|
|
331
|
+
## Upload a Chat File
|
|
100
332
|
|
|
101
333
|
```ts
|
|
102
334
|
const upload = await skyforge.uploadedFiles.upload({
|
|
103
|
-
agentBranchId:
|
|
104
|
-
chatId: "
|
|
105
|
-
file: new
|
|
335
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
336
|
+
chatId: "chat-id",
|
|
337
|
+
file: new Uint8Array([104, 101, 108, 108, 111]),
|
|
106
338
|
filename: "notes.txt",
|
|
107
339
|
contentType: "text/plain",
|
|
108
340
|
});
|
|
@@ -121,99 +353,107 @@ await skyforge.uploadedFiles.delete({
|
|
|
121
353
|
|
|
122
354
|
`filename` is required and must not be blank.
|
|
123
355
|
|
|
124
|
-
##
|
|
356
|
+
## Using With Express
|
|
125
357
|
|
|
126
|
-
|
|
358
|
+
Use your backend as the app proxy for browser chat requests. The browser calls
|
|
359
|
+
your server; your server calls Skyforge with the API token.
|
|
127
360
|
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
```
|
|
361
|
+
```ts
|
|
362
|
+
import express from "express";
|
|
363
|
+
import { SkyforgeClient } from "@runmark/sdk";
|
|
132
364
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
`Authorization: Bearer <skyforge_api_token>` through the existing
|
|
136
|
-
`/api/skyforge-chat/...` routes.
|
|
365
|
+
const app = express();
|
|
366
|
+
app.use(express.json());
|
|
137
367
|
|
|
138
|
-
|
|
139
|
-
|
|
368
|
+
const skyforge = new SkyforgeClient({
|
|
369
|
+
apiKey: process.env.SKYFORGE_API_TOKEN!,
|
|
370
|
+
baseUrl: process.env.SKYFORGE_API_BASE_URL ?? "https://api.skyforge.ai",
|
|
371
|
+
});
|
|
140
372
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
373
|
+
app.post("/api/skyforge-chat/agent_branches/:agentBranchId/messages", async (req, res, next) => {
|
|
374
|
+
try {
|
|
375
|
+
if (req.params.agentBranchId !== process.env.SKYFORGE_AGENT_BRANCH_ID) {
|
|
376
|
+
res.status(403).json({ error: { message: "Agent branch is not allowed." } });
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const data = await skyforge.agentBranches.sendMessage({
|
|
381
|
+
agentBranchId: req.params.agentBranchId,
|
|
382
|
+
chatId: req.body.chat_id,
|
|
383
|
+
externalId: req.session?.ticketId ?? req.body.external_id,
|
|
384
|
+
message: req.body.message,
|
|
385
|
+
structuredContext: req.body.structured_context,
|
|
386
|
+
retrievalScope: req.body.retrieval_scope,
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
res.json({ message: "ok", data });
|
|
390
|
+
} catch (error) {
|
|
391
|
+
next(error);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
144
394
|
```
|
|
145
395
|
|
|
146
|
-
|
|
147
|
-
|
|
396
|
+
Backend proxies can supply or override `externalId` from trusted app context,
|
|
397
|
+
such as a signed-in user session, event, ticket, project, or case ID. Do not
|
|
398
|
+
treat browser-provided `external_id` as identity.
|
|
148
399
|
|
|
149
|
-
|
|
150
|
-
cd packages/skyforge-sdk
|
|
151
|
-
|
|
152
|
-
SKYFORGE_SDK_LIVE=1 \
|
|
153
|
-
SKYFORGE_API_BASE_URL=http://localhost:3000 \
|
|
154
|
-
SKYFORGE_API_TOKEN=... \
|
|
155
|
-
SKYFORGE_AGENT_BRANCH_ID=... \
|
|
156
|
-
npm run test:live
|
|
157
|
-
```
|
|
400
|
+
## Using With Next.js Route Handlers
|
|
158
401
|
|
|
159
|
-
|
|
402
|
+
```ts
|
|
403
|
+
import { NextResponse } from "next/server";
|
|
404
|
+
import { SkyforgeClient } from "@runmark/sdk";
|
|
160
405
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
406
|
+
const skyforge = new SkyforgeClient({
|
|
407
|
+
apiKey: process.env.SKYFORGE_API_TOKEN!,
|
|
408
|
+
baseUrl: process.env.SKYFORGE_API_BASE_URL ?? "https://api.skyforge.ai",
|
|
409
|
+
});
|
|
164
410
|
|
|
165
|
-
|
|
411
|
+
export async function POST(request: Request) {
|
|
412
|
+
const body = await request.json();
|
|
413
|
+
const data = await skyforge.agentBranches.sendMessage({
|
|
414
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
415
|
+
chatId: body.chat_id,
|
|
416
|
+
externalId: body.external_id,
|
|
417
|
+
message: body.message,
|
|
418
|
+
});
|
|
166
419
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
SKYFORGE_SDK_LIVE_HITL_REQUEST_ID=...
|
|
170
|
-
SKYFORGE_SDK_LIVE_EXISTING_CHAT_ID=...
|
|
171
|
-
SKYFORGE_SDK_LIVE_TIMEOUT_MS=60000
|
|
172
|
-
SKYFORGE_SDK_LIVE_HITL_TIMEOUT_MS=120000
|
|
173
|
-
SKYFORGE_SDK_LIVE_EVENT_TIMEOUT_MS=15000
|
|
174
|
-
SKYFORGE_SDK_LIVE_MESSAGE="SDK live smoke test. Reply with a short confirmation."
|
|
175
|
-
SKYFORGE_SDK_LIVE_EXPECT_PENDING_HITL=1
|
|
176
|
-
SKYFORGE_SDK_LIVE_EXPECT_BUBBLED_HITL=1
|
|
177
|
-
SKYFORGE_SDK_LIVE_SUBMIT_PENDING_HITL=1
|
|
178
|
-
SKYFORGE_SDK_LIVE_HITL_VALUES='{"decision":"approve","notes":"Approved by SDK live test."}'
|
|
420
|
+
return NextResponse.json({ message: "ok", data });
|
|
421
|
+
}
|
|
179
422
|
```
|
|
180
423
|
|
|
181
|
-
|
|
424
|
+
## Error Handling
|
|
182
425
|
|
|
183
|
-
|
|
184
|
-
SDK package has a convenience live command for that seeded local fixture:
|
|
426
|
+
Non-2xx Skyforge API responses throw `SkyforgeApiError`.
|
|
185
427
|
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
428
|
+
```ts
|
|
429
|
+
import { SkyforgeApiError } from "@runmark/sdk";
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
await skyforge.agentBranches.getState({
|
|
433
|
+
agentBranchId: process.env.SKYFORGE_AGENT_BRANCH_ID!,
|
|
434
|
+
});
|
|
435
|
+
} catch (error) {
|
|
436
|
+
if (error instanceof SkyforgeApiError) {
|
|
437
|
+
console.error(error.status, error.code, error.requestId);
|
|
438
|
+
}
|
|
439
|
+
throw error;
|
|
440
|
+
}
|
|
191
441
|
```
|
|
192
442
|
|
|
193
|
-
|
|
443
|
+
Avoid logging API tokens, authorization headers, or full request payloads.
|
|
194
444
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
445
|
+
Successful responses must be JSON envelopes with non-null `data`; malformed,
|
|
446
|
+
empty, wrong-content-type, or oversized responses are rejected rather than
|
|
447
|
+
cast to the requested type. Error bodies are bounded before parsing.
|
|
198
448
|
|
|
199
|
-
|
|
449
|
+
## Compatibility
|
|
200
450
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
SKYFORGE_SDK_LIVE_EXPECT_BUBBLED_HITL=1
|
|
211
|
-
SKYFORGE_SDK_LIVE_SUBMIT_PENDING_HITL=1
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
`SKYFORGE_API_TOKEN` is still required and is not set by the script. Use a valid
|
|
215
|
-
local API token with access to the seeded TailYew repository/branch. You can
|
|
216
|
-
override the base URL, branch ID, message, HITL values, or timeout by setting
|
|
217
|
-
the same environment variables before running the command.
|
|
218
|
-
|
|
219
|
-
Do not expose Skyforge API tokens in browser code. This SDK is server-side only.
|
|
451
|
+
The durable request-ID, lookup, cancellation, and HITL correlation APIs are
|
|
452
|
+
additive in `0.1.2`. Existing callers may omit request IDs, but embedded and
|
|
453
|
+
retry-sensitive integrations should supply them. The SDK remains server-only
|
|
454
|
+
and throws if constructed in a browser environment.
|
|
455
|
+
|
|
456
|
+
## Related Package
|
|
457
|
+
|
|
458
|
+
Use `@runmark/agent-chat` for the browser and React chat UI. Keep this SDK on
|
|
459
|
+
the backend and expose only the app-specific proxy routes your frontend needs.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,15 +1,41 @@
|
|
|
1
|
-
import type { DeleteUploadedFileParams, GetAgentBranchStateParams, SendMessageParams, SkyforgeAgentRunEventEnvelope, SkyforgeChatMessageResponse, SkyforgeChatStateResponse, SkyforgeChatUploadedFileListResponse, SkyforgeChatUploadedFileUploadResponse, SkyforgeClientOptions, StreamAgentRunEventsParams, SubmitHumanActionParams,
|
|
1
|
+
import type { CancelAgentRunParams, CancelAgentRunResponse, CreateWebhookEndpointParams, CreateWebhookReplayParams, DeleteUploadedFileParams, GetAgentRunSourceReferencesParams, GetAgentRunParams, GetAgentBranchStateParams, GetRepositoryWebhookDeliveryParams, ListChatsParams, ListWebhookAttemptsParams, ListWebhookDeliveriesParams, ListWebhookEndpointsParams, ListRepositoryWebhookDeliveriesParams, ListUploadedFilesParams, LookupAgentRunRequestParams, SendMessageParams, SkyforgeAgentRunEventEnvelope, SkyforgeAgentRunRequestLookupResponse, SkyforgeChatSourceReferencesResponse, SkyforgeChatListResponse, SkyforgeChatMessageResponse, SkyforgeChatStateResponse, SkyforgeChatUploadedFileListResponse, SkyforgeChatUploadedFileUploadResponse, SkyforgeClientOptions, StreamAgentRunEventsParams, SubmitHumanActionParams, GetHumanActionParams, HumanActionRequestDto, JsonObject, RepositoryWebhookDeliveryDetail, RepositoryWebhookDeliveryListResponse, UpdateWebhookEndpointParams, WebhookAttemptListResponse, WebhookDeliveryListResponse, WebhookEndpointHealth, WebhookEndpointParams, WebhookEndpointSecretResponse, WebhookEndpointSummary, WebhookReplaySummary, WebhookTestDeliveryResponse, UploadFileParams } from "./types.js";
|
|
2
2
|
export declare class SkyforgeClient {
|
|
3
3
|
readonly agentBranches: {
|
|
4
4
|
getState: (params: GetAgentBranchStateParams) => Promise<SkyforgeChatStateResponse>;
|
|
5
5
|
sendMessage: (params: SendMessageParams) => Promise<SkyforgeChatMessageResponse>;
|
|
6
|
+
lookupRequest: (params: LookupAgentRunRequestParams) => Promise<SkyforgeAgentRunRequestLookupResponse>;
|
|
7
|
+
};
|
|
8
|
+
readonly chats: {
|
|
9
|
+
list: (params: ListChatsParams) => Promise<SkyforgeChatListResponse>;
|
|
6
10
|
};
|
|
7
11
|
readonly agentRuns: {
|
|
12
|
+
get: (params: GetAgentRunParams) => Promise<JsonObject>;
|
|
8
13
|
streamEvents: (params: StreamAgentRunEventsParams) => Promise<AsyncIterable<SkyforgeAgentRunEventEnvelope>>;
|
|
14
|
+
getSourceReferences: (params: GetAgentRunSourceReferencesParams) => Promise<SkyforgeChatSourceReferencesResponse>;
|
|
15
|
+
cancel: (params: CancelAgentRunParams) => Promise<CancelAgentRunResponse>;
|
|
9
16
|
};
|
|
10
17
|
readonly humanActions: {
|
|
18
|
+
get: (params: GetHumanActionParams) => Promise<HumanActionRequestDto>;
|
|
11
19
|
submit: (params: SubmitHumanActionParams) => Promise<SkyforgeChatMessageResponse>;
|
|
12
20
|
};
|
|
21
|
+
readonly webhooks: {
|
|
22
|
+
create: (params: CreateWebhookEndpointParams) => Promise<WebhookEndpointSecretResponse>;
|
|
23
|
+
list: (params: ListWebhookEndpointsParams) => Promise<WebhookEndpointSummary[]>;
|
|
24
|
+
get: (params: WebhookEndpointParams) => Promise<WebhookEndpointSummary>;
|
|
25
|
+
update: (params: UpdateWebhookEndpointParams) => Promise<WebhookEndpointSummary>;
|
|
26
|
+
delete: (params: WebhookEndpointParams) => Promise<string>;
|
|
27
|
+
enable: (params: WebhookEndpointParams) => Promise<WebhookEndpointSummary>;
|
|
28
|
+
disable: (params: WebhookEndpointParams) => Promise<WebhookEndpointSummary>;
|
|
29
|
+
rotateSecret: (params: WebhookEndpointParams) => Promise<WebhookEndpointSecretResponse>;
|
|
30
|
+
test: (params: WebhookEndpointParams) => Promise<WebhookTestDeliveryResponse>;
|
|
31
|
+
health: (params: WebhookEndpointParams) => Promise<WebhookEndpointHealth>;
|
|
32
|
+
listDeliveries: (params: ListWebhookDeliveriesParams) => Promise<WebhookDeliveryListResponse>;
|
|
33
|
+
listAttempts: (params: ListWebhookAttemptsParams) => Promise<WebhookAttemptListResponse>;
|
|
34
|
+
listRepositoryDeliveries: (params: ListRepositoryWebhookDeliveriesParams) => Promise<RepositoryWebhookDeliveryListResponse>;
|
|
35
|
+
getDelivery: (params: GetRepositoryWebhookDeliveryParams) => Promise<RepositoryWebhookDeliveryDetail>;
|
|
36
|
+
replay: (params: CreateWebhookReplayParams) => Promise<WebhookReplaySummary>;
|
|
37
|
+
listReplays: (params: WebhookEndpointParams) => Promise<WebhookReplaySummary[]>;
|
|
38
|
+
};
|
|
13
39
|
readonly uploadedFiles: {
|
|
14
40
|
list: (params: ListUploadedFilesParams) => Promise<SkyforgeChatUploadedFileListResponse>;
|
|
15
41
|
upload: (params: UploadFileParams) => Promise<SkyforgeChatUploadedFileUploadResponse>;
|
|
@@ -18,9 +44,30 @@ export declare class SkyforgeClient {
|
|
|
18
44
|
private readonly baseUrl;
|
|
19
45
|
private readonly http;
|
|
20
46
|
constructor(options: SkyforgeClientOptions);
|
|
47
|
+
private getAgentRun;
|
|
48
|
+
private getHumanAction;
|
|
49
|
+
private createWebhookEndpoint;
|
|
50
|
+
private listWebhookEndpoints;
|
|
51
|
+
private getWebhookEndpoint;
|
|
52
|
+
private updateWebhookEndpoint;
|
|
53
|
+
private deleteWebhookEndpoint;
|
|
54
|
+
private webhookEndpointAction;
|
|
55
|
+
private rotateWebhookSecret;
|
|
56
|
+
private testWebhookEndpoint;
|
|
57
|
+
private getWebhookHealth;
|
|
58
|
+
private listWebhookDeliveries;
|
|
59
|
+
private listWebhookAttempts;
|
|
60
|
+
private listRepositoryWebhookDeliveries;
|
|
61
|
+
private getRepositoryWebhookDelivery;
|
|
62
|
+
private replayWebhookEvents;
|
|
63
|
+
private listWebhookReplays;
|
|
21
64
|
private getAgentBranchState;
|
|
65
|
+
private listChats;
|
|
22
66
|
private sendMessage;
|
|
67
|
+
private lookupRequest;
|
|
23
68
|
private streamEvents;
|
|
69
|
+
private getSourceReferences;
|
|
70
|
+
private cancelAgentRun;
|
|
24
71
|
private submitHumanAction;
|
|
25
72
|
private listUploadedFiles;
|
|
26
73
|
private uploadFile;
|