@cerebro-labs/agentnava-sdk 0.26.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/LICENSE +202 -0
- package/NOTICE +12 -0
- package/README.md +230 -0
- package/agent.d.ts +285 -0
- package/agent.js +394 -0
- package/agent.js.map +1 -0
- package/attach.d.ts +49 -0
- package/attach.js +115 -0
- package/attach.js.map +1 -0
- package/connection-status.d.ts +28 -0
- package/connection-status.js +61 -0
- package/connection-status.js.map +1 -0
- package/connection.d.ts +89 -0
- package/connection.js +143 -0
- package/connection.js.map +1 -0
- package/conversation.d.ts +177 -0
- package/conversation.js +348 -0
- package/conversation.js.map +1 -0
- package/end-user.d.ts +159 -0
- package/end-user.js +209 -0
- package/end-user.js.map +1 -0
- package/errors.d.ts +43 -0
- package/errors.js +55 -0
- package/errors.js.map +1 -0
- package/http.d.ts +32 -0
- package/http.js +192 -0
- package/http.js.map +1 -0
- package/import-tools.d.ts +282 -0
- package/import-tools.js +686 -0
- package/import-tools.js.map +1 -0
- package/index.d.ts +16 -0
- package/index.js +38 -0
- package/index.js.map +1 -0
- package/knowledge-base.d.ts +165 -0
- package/knowledge-base.js +274 -0
- package/knowledge-base.js.map +1 -0
- package/load-agent-dir.d.ts +17 -0
- package/load-agent-dir.js +192 -0
- package/load-agent-dir.js.map +1 -0
- package/package.json +32 -0
- package/project.d.ts +94 -0
- package/project.js +155 -0
- package/project.js.map +1 -0
- package/secret.d.ts +45 -0
- package/secret.js +69 -0
- package/secret.js.map +1 -0
- package/templates-inline.d.ts +2 -0
- package/templates-inline.js +16 -0
- package/templates-inline.js.map +1 -0
- package/templates.d.ts +16 -0
- package/templates.js +32 -0
- package/templates.js.map +1 -0
- package/turn.d.ts +11 -0
- package/turn.js +244 -0
- package/turn.js.map +1 -0
- package/types.d.ts +485 -0
- package/types.js +4 -0
- package/types.js.map +1 -0
- package/version.d.ts +1 -0
- package/version.js +5 -0
- package/version.js.map +1 -0
- package/workflows.d.ts +36 -0
- package/workflows.js +101 -0
- package/workflows.js.map +1 -0
- package/workspace.d.ts +290 -0
- package/workspace.js +390 -0
- package/workspace.js.map +1 -0
package/types.d.ts
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
export type Grade = 'standard' | 'premium';
|
|
2
|
+
export type Filesystem = 'drive' | 'none';
|
|
3
|
+
/** Where a credential is supplied.
|
|
4
|
+
*
|
|
5
|
+
* 'fixed' is set once on the agent and used by every conversation. 'conversation'
|
|
6
|
+
* is asked for when a conversation starts, so each user brings their own. The
|
|
7
|
+
* two DO NOT substitute for each other: an account connected on the agent does
|
|
8
|
+
* not satisfy a declaration scoped 'conversation'. */
|
|
9
|
+
export type Scope = 'fixed' | 'conversation';
|
|
10
|
+
/** WHOSE credential a stored account or value is, which is a different question
|
|
11
|
+
* from `Scope` above. `Scope` is what an agent DECLARES it needs; this is who
|
|
12
|
+
* a row that exists actually belongs to.
|
|
13
|
+
*
|
|
14
|
+
* 'endUser' is one of YOUR end users: the person your application serves,
|
|
15
|
+
* never a member of this workspace. The two are separate populations with
|
|
16
|
+
* separate id namespaces: see `EndUser`. */
|
|
17
|
+
export type CredentialOwner = 'workspace' | 'agent' | 'endUser' | 'conversation';
|
|
18
|
+
export interface WorkspaceInfo {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
credits: number;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
}
|
|
24
|
+
/** One procedure the agent follows when its `when` matches what is in front of
|
|
25
|
+
* it. `when` is one line and is not optional: it is what the agent reads to
|
|
26
|
+
* decide the workflow applies, so a vague one makes a good workflow invisible. */
|
|
27
|
+
export interface Workflow {
|
|
28
|
+
name: string;
|
|
29
|
+
when: string;
|
|
30
|
+
content: string;
|
|
31
|
+
}
|
|
32
|
+
export type Trigger = {
|
|
33
|
+
kind: 'chat';
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'schedule';
|
|
36
|
+
cron: string;
|
|
37
|
+
timezone?: string;
|
|
38
|
+
message: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'interval';
|
|
41
|
+
seconds: number;
|
|
42
|
+
maxRuns?: number;
|
|
43
|
+
message: string;
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'webhook';
|
|
46
|
+
message?: string;
|
|
47
|
+
url?: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* What an agent NEEDS, not what it has.
|
|
51
|
+
*
|
|
52
|
+
* Named `Connection` until the actual account became an object you hold. Two
|
|
53
|
+
* different things cannot share one name: this one says "this agent requires
|
|
54
|
+
* Gmail, supplied per conversation", and says nothing about whether anybody has
|
|
55
|
+
* connected. `Connection` is the account itself, with tokens behind it.
|
|
56
|
+
*
|
|
57
|
+
* The distinction is the one `ConnectionStatus.declared` versus `.connected`
|
|
58
|
+
* already draws, now in the type names too.
|
|
59
|
+
*/
|
|
60
|
+
export interface ConnectionRequirement {
|
|
61
|
+
provider: string;
|
|
62
|
+
scope: Scope;
|
|
63
|
+
required?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** What an agent NEEDS, not the stored value. `Secret` is the value. */
|
|
66
|
+
export interface SecretRequirement {
|
|
67
|
+
name: string;
|
|
68
|
+
scope: Scope;
|
|
69
|
+
required?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface Parameter {
|
|
72
|
+
name: string;
|
|
73
|
+
type: 'string' | 'number' | 'date' | 'choice';
|
|
74
|
+
required?: boolean;
|
|
75
|
+
default?: string;
|
|
76
|
+
choices?: string[];
|
|
77
|
+
}
|
|
78
|
+
export interface McpServer {
|
|
79
|
+
name: string;
|
|
80
|
+
url: string;
|
|
81
|
+
transport?: 'http' | 'sse';
|
|
82
|
+
headers?: Record<string, string>;
|
|
83
|
+
timeoutMs?: number;
|
|
84
|
+
}
|
|
85
|
+
export interface HttpToolParam {
|
|
86
|
+
type: 'string' | 'number' | 'boolean';
|
|
87
|
+
description?: string;
|
|
88
|
+
required?: boolean;
|
|
89
|
+
choices?: (string | number)[];
|
|
90
|
+
}
|
|
91
|
+
export interface HttpTool {
|
|
92
|
+
name: string;
|
|
93
|
+
description: string;
|
|
94
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
95
|
+
url: string;
|
|
96
|
+
headers?: Record<string, string>;
|
|
97
|
+
body?: string;
|
|
98
|
+
params?: Record<string, HttpToolParam>;
|
|
99
|
+
secrets?: string[];
|
|
100
|
+
timeoutMs?: number;
|
|
101
|
+
}
|
|
102
|
+
export interface AgentData {
|
|
103
|
+
id: string;
|
|
104
|
+
name: string;
|
|
105
|
+
instructions: string;
|
|
106
|
+
greeting?: string;
|
|
107
|
+
grade: Grade;
|
|
108
|
+
filesystem: Filesystem;
|
|
109
|
+
tools: string[];
|
|
110
|
+
workflows: Workflow[];
|
|
111
|
+
connections: ConnectionRequirement[];
|
|
112
|
+
secrets: SecretRequirement[];
|
|
113
|
+
parameters: Parameter[];
|
|
114
|
+
triggers: Trigger[];
|
|
115
|
+
mcpServers: McpServer[];
|
|
116
|
+
httpTools: HttpTool[];
|
|
117
|
+
/** Workspace knowledge bases this agent reads, by key. Always present, empty
|
|
118
|
+
* when none are attached, so a caller never has to distinguish "none" from
|
|
119
|
+
* "an older server that did not send the field". */
|
|
120
|
+
knowledgeBases: string[];
|
|
121
|
+
version: number;
|
|
122
|
+
createdAt: string;
|
|
123
|
+
updatedAt: string;
|
|
124
|
+
}
|
|
125
|
+
/** Whether a provider is actually usable right now, as opposed to declared.
|
|
126
|
+
*
|
|
127
|
+
* `declared` and `connected` are separate on purpose: an agent can declare
|
|
128
|
+
* HubSpot and have nobody connected, which is the state worth showing on a
|
|
129
|
+
* setup checklist. */
|
|
130
|
+
export interface ConnectionStatus {
|
|
131
|
+
/** The connection row's id, which `disconnect()` revokes by. */
|
|
132
|
+
id?: string;
|
|
133
|
+
provider: string;
|
|
134
|
+
/** 'connected' once the account is attached; anything else means it is not
|
|
135
|
+
* usable yet. `connected` below is the boolean form of this. */
|
|
136
|
+
status?: string;
|
|
137
|
+
/** The conversation this account belongs to, or null for the agent's shared
|
|
138
|
+
* account. `scope` and `suppliedBy` are both derived from it. */
|
|
139
|
+
conversationId?: string | null;
|
|
140
|
+
/** The agent this account belongs to. */
|
|
141
|
+
agentId?: string | null;
|
|
142
|
+
/** One of YOUR end users, when the account is theirs. Their id, from your
|
|
143
|
+
* database, never a workspace member's. Null on a shared account. */
|
|
144
|
+
endUserId?: string | null;
|
|
145
|
+
/** Whether the AGENT DECLARES it needs this provider, and whether it is
|
|
146
|
+
* mandatory.
|
|
147
|
+
*
|
|
148
|
+
* OPTIONAL because only one of the two endpoints can answer. `agent
|
|
149
|
+
* .connections()` reads the MERGED view (`/v1/agents/<id>/connections`),
|
|
150
|
+
* where the server has joined the agent's declarations against the live
|
|
151
|
+
* rows, and both are real there. `ws.connections.list()` and
|
|
152
|
+
* `endUser(id).connections.list()` read raw rows (`/v1/connections`), which
|
|
153
|
+
* know nothing about any agent's configuration, so both are absent.
|
|
154
|
+
*
|
|
155
|
+
* They used to be declared as required booleans, which meant the raw-row
|
|
156
|
+
* paths had to invent them. They were filled with the constants `true` and
|
|
157
|
+
* `false`, so every row claimed to be declared and none claimed to be
|
|
158
|
+
* required. Absent is the honest answer, and undefined reads as "this
|
|
159
|
+
* endpoint cannot tell you" rather than as "no". */
|
|
160
|
+
declared?: boolean;
|
|
161
|
+
required?: boolean;
|
|
162
|
+
/** What the agent DECLARED it needs. Whose account this actually is, is
|
|
163
|
+
* `owner`, and the two are not the same question. */
|
|
164
|
+
scope?: Scope;
|
|
165
|
+
/** Whose account this is. Derived from which of the three id fields above
|
|
166
|
+
* is set, so it is correct even on a server that does not send it. */
|
|
167
|
+
owner?: CredentialOwner;
|
|
168
|
+
connected: boolean;
|
|
169
|
+
/** Which of the three scopes supplied the account. 'endUser' is one of YOUR
|
|
170
|
+
* users, whose account outlives any single conversation. */
|
|
171
|
+
suppliedBy?: 'conversation' | 'agent' | 'endUser' | 'workspace';
|
|
172
|
+
accountLabel?: string;
|
|
173
|
+
error?: string;
|
|
174
|
+
}
|
|
175
|
+
/** Something a conversation is still missing before it can work. */
|
|
176
|
+
export interface Requirement {
|
|
177
|
+
kind: 'secret' | 'connection';
|
|
178
|
+
name: string;
|
|
179
|
+
required: boolean;
|
|
180
|
+
}
|
|
181
|
+
export interface AgentConversationData {
|
|
182
|
+
id: string;
|
|
183
|
+
agentId: string;
|
|
184
|
+
version: number;
|
|
185
|
+
startedAt: string;
|
|
186
|
+
title?: string;
|
|
187
|
+
/** True when nothing REQUIRED is outstanding. An optional gap leaves this
|
|
188
|
+
* true, because the conversation genuinely does work without it. */
|
|
189
|
+
ready: boolean;
|
|
190
|
+
requires: Requirement[];
|
|
191
|
+
}
|
|
192
|
+
export interface ProjectConversationData {
|
|
193
|
+
id: string;
|
|
194
|
+
projectId: string;
|
|
195
|
+
startedAt: string;
|
|
196
|
+
title?: string;
|
|
197
|
+
ready: boolean;
|
|
198
|
+
requires: Requirement[];
|
|
199
|
+
}
|
|
200
|
+
/** An AGENT on a project, not a person.
|
|
201
|
+
*
|
|
202
|
+
* Called `ProjectAgent` and not `Member`, which is what it was. `Member` is
|
|
203
|
+
* the word this product uses for a HUMAN on the account, so a public type of
|
|
204
|
+
* that name holding an `agentId` was the same trap as `userId` meaning two
|
|
205
|
+
* populations: `import { Member }` handed you the agent one, and the human one
|
|
206
|
+
* had to carry a prefix to get out of its way.
|
|
207
|
+
*
|
|
208
|
+
* The people are `WorkspaceMember`, from `ws.members()`. Nothing here is a
|
|
209
|
+
* person. */
|
|
210
|
+
export interface ProjectAgent {
|
|
211
|
+
agentId: string;
|
|
212
|
+
name: string;
|
|
213
|
+
addedAt: string;
|
|
214
|
+
}
|
|
215
|
+
export interface ProjectData {
|
|
216
|
+
id: string;
|
|
217
|
+
name: string;
|
|
218
|
+
goal: string | null;
|
|
219
|
+
managerInstructions: string | null;
|
|
220
|
+
grade: Grade;
|
|
221
|
+
status: 'active' | 'archived';
|
|
222
|
+
agents: ProjectAgent[];
|
|
223
|
+
createdAt: string;
|
|
224
|
+
updatedAt: string;
|
|
225
|
+
}
|
|
226
|
+
export interface Step {
|
|
227
|
+
id: string;
|
|
228
|
+
label: string;
|
|
229
|
+
kind: 'read' | 'write' | 'edit' | 'search' | 'shell' | 'web' | 'data' | 'task' | 'plan' | 'other';
|
|
230
|
+
status: 'active' | 'done' | 'error';
|
|
231
|
+
}
|
|
232
|
+
export type TurnEvent = {
|
|
233
|
+
type: 'start';
|
|
234
|
+
turnId: string;
|
|
235
|
+
}
|
|
236
|
+
/** Carries the WHOLE step list every time, not a change to it. Replace what
|
|
237
|
+
* you are showing rather than merging; that is what makes reconnecting
|
|
238
|
+
* safe. */
|
|
239
|
+
| {
|
|
240
|
+
type: 'progress';
|
|
241
|
+
steps: Step[];
|
|
242
|
+
} | {
|
|
243
|
+
type: 'tool';
|
|
244
|
+
name: string;
|
|
245
|
+
state: 'start' | 'end';
|
|
246
|
+
ok?: boolean;
|
|
247
|
+
}
|
|
248
|
+
/** A fragment, not a line. Append; do not assemble. */
|
|
249
|
+
| {
|
|
250
|
+
type: 'text';
|
|
251
|
+
text: string;
|
|
252
|
+
} | {
|
|
253
|
+
type: 'file';
|
|
254
|
+
path: string;
|
|
255
|
+
} | {
|
|
256
|
+
type: 'done';
|
|
257
|
+
reply: string;
|
|
258
|
+
} | {
|
|
259
|
+
type: 'error';
|
|
260
|
+
message: string;
|
|
261
|
+
retryable: boolean;
|
|
262
|
+
};
|
|
263
|
+
export interface Message {
|
|
264
|
+
role: 'user' | 'assistant';
|
|
265
|
+
content: string;
|
|
266
|
+
at: string;
|
|
267
|
+
}
|
|
268
|
+
/** An image goes to the model as an image. Anything else is written into the
|
|
269
|
+
* conversation's drive before the turn starts, so the agent can parse it,
|
|
270
|
+
* compute over it, and still have it next turn. */
|
|
271
|
+
export interface Attachment {
|
|
272
|
+
kind: 'image' | 'file';
|
|
273
|
+
name?: string;
|
|
274
|
+
mimeType: string;
|
|
275
|
+
/** base64, without a data: prefix. */
|
|
276
|
+
data: string;
|
|
277
|
+
}
|
|
278
|
+
/** A knowledge base: a named folder of files at WORKSPACE scope that an agent
|
|
279
|
+
* configuration attaches by key. */
|
|
280
|
+
export interface KnowledgeBaseData {
|
|
281
|
+
/** Workspace-unique. This is what an agent configuration stores, and it is
|
|
282
|
+
* not editable, because renaming it would move every attachment silently. */
|
|
283
|
+
key: string;
|
|
284
|
+
name: string;
|
|
285
|
+
description?: string;
|
|
286
|
+
/** Counted when read, so it never disagrees with the files it counts. */
|
|
287
|
+
fileCount: number;
|
|
288
|
+
createdAt: string;
|
|
289
|
+
updatedAt: string;
|
|
290
|
+
}
|
|
291
|
+
/** One file in a knowledge base. PATH, not a flat name: a base is a folder, and
|
|
292
|
+
* the session mount reproduces the shape verbatim. */
|
|
293
|
+
/**
|
|
294
|
+
* Whether an agent can read a file in a knowledge base.
|
|
295
|
+
*
|
|
296
|
+
* Text and markdown are readable as they stand. A PDF, a scan or a spreadsheet
|
|
297
|
+
* has to be turned into text first, which happens when the file is uploaded
|
|
298
|
+
* rather than when a question is asked, so an agent never waits on it.
|
|
299
|
+
*
|
|
300
|
+
* 'readable' an agent can read this now
|
|
301
|
+
* 'processing' being prepared, check back shortly
|
|
302
|
+
* 'unreadable' cannot be read, and `reason` says why
|
|
303
|
+
*/
|
|
304
|
+
export type KnowledgeFileStatus = 'readable' | 'processing' | 'unreadable';
|
|
305
|
+
export interface KnowledgeFileEntry {
|
|
306
|
+
path: string;
|
|
307
|
+
mimeType: string;
|
|
308
|
+
size: number;
|
|
309
|
+
createdAt: string;
|
|
310
|
+
updatedAt: string;
|
|
311
|
+
/** Whether an agent can read this file. */
|
|
312
|
+
status: KnowledgeFileStatus;
|
|
313
|
+
/** Why it cannot be read, as a sentence you could show someone.
|
|
314
|
+
*
|
|
315
|
+
* The same text reaches the agent in place of the file, so a developer
|
|
316
|
+
* reading this and a model reading the base get one explanation rather than
|
|
317
|
+
* two that can drift apart. Absent unless `status` is 'unreadable'. */
|
|
318
|
+
reason?: string;
|
|
319
|
+
}
|
|
320
|
+
export interface FileEntry {
|
|
321
|
+
path: string;
|
|
322
|
+
kind: 'file' | 'directory';
|
|
323
|
+
mimeType: string;
|
|
324
|
+
size: number;
|
|
325
|
+
modifiedAt: string;
|
|
326
|
+
}
|
|
327
|
+
/** Fields accepted when creating or updating an agent. Everything is optional on
|
|
328
|
+
* an update: what you do not send is inherited from the current version, which
|
|
329
|
+
* is the whole reason updating is safe. */
|
|
330
|
+
export interface AgentFields {
|
|
331
|
+
id?: string;
|
|
332
|
+
name?: string;
|
|
333
|
+
instructions?: string;
|
|
334
|
+
greeting?: string;
|
|
335
|
+
grade?: Grade;
|
|
336
|
+
filesystem?: Filesystem;
|
|
337
|
+
/**
|
|
338
|
+
* Knowledge bases this agent reads, by workspace-unique key.
|
|
339
|
+
*
|
|
340
|
+
* Mounted READ-ONLY at `/knowledge/<key>/` when a session starts, so the
|
|
341
|
+
* agent finds things with `ls`, `grep` and `cat` rather than a retrieval API.
|
|
342
|
+
*
|
|
343
|
+
* Which bases an agent reads is part of its CONFIGURATION, so changing this
|
|
344
|
+
* publishes a version you can diff and roll back. The CONTENTS of a base are
|
|
345
|
+
* not versioned: correcting a file changes what every attached agent sees on
|
|
346
|
+
* its next turn, which is the same by-reference promise `agent.knowledge`
|
|
347
|
+
* already makes.
|
|
348
|
+
*
|
|
349
|
+
* A key that names no base is refused when the configuration is saved, and
|
|
350
|
+
* deleting a base is refused while any agent attaches it, so an attachment
|
|
351
|
+
* that was valid stays valid.
|
|
352
|
+
*/
|
|
353
|
+
knowledgeBases?: string[];
|
|
354
|
+
tools?: string[];
|
|
355
|
+
workflows?: Workflow[];
|
|
356
|
+
connections?: ConnectionRequirement[];
|
|
357
|
+
secrets?: SecretRequirement[];
|
|
358
|
+
parameters?: Parameter[];
|
|
359
|
+
triggers?: Trigger[];
|
|
360
|
+
mcpServers?: McpServer[];
|
|
361
|
+
httpTools?: HttpTool[];
|
|
362
|
+
}
|
|
363
|
+
export interface ProjectFields {
|
|
364
|
+
name?: string;
|
|
365
|
+
goal?: string;
|
|
366
|
+
managerInstructions?: string;
|
|
367
|
+
grade?: Grade;
|
|
368
|
+
agents?: string[];
|
|
369
|
+
}
|
|
370
|
+
/** Values supplied when a conversation starts: the credentials its agent
|
|
371
|
+
* declared at 'conversation' scope, plus any parameters. */
|
|
372
|
+
export interface Bindings {
|
|
373
|
+
secrets?: Record<string, string>;
|
|
374
|
+
connections?: Record<string, string>;
|
|
375
|
+
}
|
|
376
|
+
/** One markdown file read off disk: a workflow, a subagent, an imported skill.
|
|
377
|
+
*
|
|
378
|
+
* `when` is OPTIONAL here and required at publish. A file on disk has no `when`
|
|
379
|
+
* until someone writes one, and refusing to read it would be the wrong place to
|
|
380
|
+
* say so; the API says it at publish, with a message naming the file. */
|
|
381
|
+
export interface SpecFile {
|
|
382
|
+
name: string;
|
|
383
|
+
content: string;
|
|
384
|
+
when?: string;
|
|
385
|
+
}
|
|
386
|
+
/** What a loaded agent directory produces: the fields `ws.agents.create()` takes,
|
|
387
|
+
* plus the id the caller chose and the few legacy names the loader still reads
|
|
388
|
+
* off an existing directory. */
|
|
389
|
+
export interface SpecInput extends Omit<AgentFields, 'workflows'> {
|
|
390
|
+
key: string;
|
|
391
|
+
workflows?: SpecFile[];
|
|
392
|
+
skills?: SpecFile[];
|
|
393
|
+
subagents?: SpecFile[];
|
|
394
|
+
widgets?: {
|
|
395
|
+
name: string;
|
|
396
|
+
[k: string]: unknown;
|
|
397
|
+
}[];
|
|
398
|
+
sandbox?: Filesystem;
|
|
399
|
+
role?: string;
|
|
400
|
+
/** Retired in favour of `tools: ['web_search']`, still read off an existing
|
|
401
|
+
* agent directory so one written before the rename still loads. */
|
|
402
|
+
webSearch?: boolean;
|
|
403
|
+
}
|
|
404
|
+
/** A PERSON with access to this workspace: your colleague, on a seat.
|
|
405
|
+
*
|
|
406
|
+
* NOT one of your end users, and the difference is the sharpest naming trap in
|
|
407
|
+
* this SDK. A workspace member signs in here, we issued their id, and we
|
|
408
|
+
* authenticate it. A `EndUser` is the person YOUR application serves, never
|
|
409
|
+
* signs in here, and carries an id from YOUR database that we store verbatim
|
|
410
|
+
* and never check.
|
|
411
|
+
*
|
|
412
|
+
* `userId` below is therefore a PLATFORM id and is not interchangeable with
|
|
413
|
+
* `agent.start({ endUserId })`. Both fields were once spelled `userId`, which
|
|
414
|
+
* made passing one where the other belonged a call that could not fail: a
|
|
415
|
+
* end-user id is never validated against anything, so it silently labelled a
|
|
416
|
+
* conversation with a colleague's internal id. The two names now differ.
|
|
417
|
+
*
|
|
418
|
+
* Named WorkspaceMember rather than Member because a public `Member` type used
|
|
419
|
+
* to mean an AGENT attached to a project. That one is now `ProjectAgent`, so
|
|
420
|
+
* the prefix here is no longer load-bearing; it stays because it says which of
|
|
421
|
+
* the two populations this is at every call site, and that is worth four
|
|
422
|
+
* characters.
|
|
423
|
+
*
|
|
424
|
+
* Read-only, like everything else on the account surface: the SDK can see who
|
|
425
|
+
* is on the account and what they may do, and cannot add, remove or re-role
|
|
426
|
+
* anyone. Those are decisions a person makes while signed in, not something a
|
|
427
|
+
* key should be able to do on their behalf. */
|
|
428
|
+
export interface WorkspaceMember {
|
|
429
|
+
/** A PLATFORM user id (`usr_...`), issued by us. Never pass this to
|
|
430
|
+
* `agent.start({ endUserId })` or `ws.endUser()`. */
|
|
431
|
+
userId: string;
|
|
432
|
+
email: string;
|
|
433
|
+
name: string | null;
|
|
434
|
+
/** 'owner' is the account's creator; 'admin' can manage it; 'member' cannot. */
|
|
435
|
+
role: 'owner' | 'admin' | 'member';
|
|
436
|
+
/** Unix seconds. */
|
|
437
|
+
joinedAt: number;
|
|
438
|
+
}
|
|
439
|
+
/** What is left to spend.
|
|
440
|
+
*
|
|
441
|
+
* `credits` is the balance in credits, where one credit is one US dollar. The
|
|
442
|
+
* turn counts are the same balance expressed in the unit people actually
|
|
443
|
+
* reason with, because "12.5 credits" answers nothing on its own. */
|
|
444
|
+
export interface Credits {
|
|
445
|
+
credits: number;
|
|
446
|
+
standardTurnsRemaining: number;
|
|
447
|
+
premiumTurnsRemaining: number;
|
|
448
|
+
}
|
|
449
|
+
/** The seat plan this workspace is on.
|
|
450
|
+
*
|
|
451
|
+
* Seats are a SEPARATE purchase from credits: a plan says how many people may
|
|
452
|
+
* share the account, credits pay for the work their agents do. Being on the
|
|
453
|
+
* free plan does not make turns free. */
|
|
454
|
+
export interface SeatPlan {
|
|
455
|
+
key: string;
|
|
456
|
+
label: string;
|
|
457
|
+
/** People allowed. `null` means negotiated, which is NOT unlimited and
|
|
458
|
+
* should never be shown to anyone as a number. */
|
|
459
|
+
seats: number | null;
|
|
460
|
+
/** Monthly price in US cents. `null` means there is no published price and
|
|
461
|
+
* the answer is a conversation, which is the opposite fact from 0 (free). */
|
|
462
|
+
usdCentsPerMonth: number | null;
|
|
463
|
+
}
|
|
464
|
+
/** A stored secret, WITHOUT its value.
|
|
465
|
+
*
|
|
466
|
+
* The value is never returned by any read. It is written once, encrypted at
|
|
467
|
+
* rest, and substituted into an HTTP tool's url, headers or body server-side at
|
|
468
|
+
* call time, so it is never part of anything the model can see or repeat. To
|
|
469
|
+
* change one, write it again; to be sure of one, replace it. */
|
|
470
|
+
export interface SecretMeta {
|
|
471
|
+
id: string;
|
|
472
|
+
name: string;
|
|
473
|
+
description?: string | null;
|
|
474
|
+
/** The agent this secret is scoped to, or null when the whole workspace
|
|
475
|
+
* reads it. An agent-scoped secret of the same name wins. */
|
|
476
|
+
agentId?: string | null;
|
|
477
|
+
/** The end user this secret belongs to, or null when it is the workspace's.
|
|
478
|
+
* One of YOUR user ids, stored verbatim: we never resolve or authenticate
|
|
479
|
+
* it. A secret with one can only be attached to a conversation launched for
|
|
480
|
+
* that same person. */
|
|
481
|
+
endUserId?: string | null;
|
|
482
|
+
createdAt?: string;
|
|
483
|
+
updatedAt?: string;
|
|
484
|
+
lastUsedAt?: string | null;
|
|
485
|
+
}
|
package/types.js
ADDED
package/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4DAA4D","sourcesContent":["// The shapes this SDK hands back. Transcribed from design/sdk-design.md's own\n// Types section so the reference and the code cannot drift.\n\nexport type Grade = 'standard' | 'premium';\nexport type Filesystem = 'drive' | 'none';\n\n/** Where a credential is supplied.\n *\n * 'fixed' is set once on the agent and used by every conversation. 'conversation'\n * is asked for when a conversation starts, so each user brings their own. The\n * two DO NOT substitute for each other: an account connected on the agent does\n * not satisfy a declaration scoped 'conversation'. */\nexport type Scope = 'fixed' | 'conversation';\n\n/** WHOSE credential a stored account or value is, which is a different question\n * from `Scope` above. `Scope` is what an agent DECLARES it needs; this is who\n * a row that exists actually belongs to.\n *\n * 'endUser' is one of YOUR end users: the person your application serves,\n * never a member of this workspace. The two are separate populations with\n * separate id namespaces: see `EndUser`. */\nexport type CredentialOwner = 'workspace' | 'agent' | 'endUser' | 'conversation';\n\nexport interface WorkspaceInfo {\n\tid: string;\n\tname: string;\n\tcredits: number;\n\tcreatedAt: string;\n}\n\n/** One procedure the agent follows when its `when` matches what is in front of\n * it. `when` is one line and is not optional: it is what the agent reads to\n * decide the workflow applies, so a vague one makes a good workflow invisible. */\nexport interface Workflow {\n\tname: string;\n\twhen: string;\n\tcontent: string;\n}\n\n// `SessionMode` is deliberately NOT exposed.\n//\n// The API has one ('per-user' | 'per-call') and 'per-user' makes the server\n// resume a person's most recent conversation. That is the behaviour `start`\n// refuses to have: a verb that sometimes returns something you already had, and\n// which has to guess which one the moment a person has two.\n//\n// Its other effect, fanning a scheduled firing out across the people who have\n// conversations with an agent, is real and worth exposing one day. It should\n// arrive under a name that says THAT, not under a mode that also silently\n// changes what `start` returns.\n\nexport type Trigger =\n\t| { kind: 'chat' }\n\t| { kind: 'schedule'; cron: string; timezone?: string; message: string }\n\t| { kind: 'interval'; seconds: number; maxRuns?: number; message: string }\n\t| { kind: 'webhook'; message?: string; url?: string };\n\n/**\n * What an agent NEEDS, not what it has.\n *\n * Named `Connection` until the actual account became an object you hold. Two\n * different things cannot share one name: this one says \"this agent requires\n * Gmail, supplied per conversation\", and says nothing about whether anybody has\n * connected. `Connection` is the account itself, with tokens behind it.\n *\n * The distinction is the one `ConnectionStatus.declared` versus `.connected`\n * already draws, now in the type names too.\n */\nexport interface ConnectionRequirement {\n\tprovider: string;\n\tscope: Scope;\n\trequired?: boolean;\n}\n\n/** What an agent NEEDS, not the stored value. `Secret` is the value. */\nexport interface SecretRequirement {\n\tname: string;\n\tscope: Scope;\n\trequired?: boolean;\n}\n\nexport interface Parameter {\n\tname: string;\n\ttype: 'string' | 'number' | 'date' | 'choice';\n\trequired?: boolean;\n\tdefault?: string;\n\tchoices?: string[];\n}\n\nexport interface McpServer {\n\tname: string;\n\turl: string;\n\ttransport?: 'http' | 'sse';\n\theaders?: Record<string, string>;\n\ttimeoutMs?: number;\n}\n\nexport interface HttpToolParam {\n\ttype: 'string' | 'number' | 'boolean';\n\tdescription?: string;\n\trequired?: boolean;\n\tchoices?: (string | number)[];\n}\n\nexport interface HttpTool {\n\tname: string;\n\tdescription: string;\n\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\turl: string;\n\theaders?: Record<string, string>;\n\tbody?: string;\n\tparams?: Record<string, HttpToolParam>;\n\tsecrets?: string[];\n\ttimeoutMs?: number;\n}\n\nexport interface AgentData {\n\tid: string;\n\tname: string;\n\tinstructions: string;\n\tgreeting?: string;\n\tgrade: Grade;\n\tfilesystem: Filesystem;\n\ttools: string[];\n\tworkflows: Workflow[];\n\tconnections: ConnectionRequirement[];\n\tsecrets: SecretRequirement[];\n\tparameters: Parameter[];\n\ttriggers: Trigger[];\n\tmcpServers: McpServer[];\n\thttpTools: HttpTool[];\n\t/** Workspace knowledge bases this agent reads, by key. Always present, empty\n\t * when none are attached, so a caller never has to distinguish \"none\" from\n\t * \"an older server that did not send the field\". */\n\tknowledgeBases: string[];\n\tversion: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\n/** Whether a provider is actually usable right now, as opposed to declared.\n *\n * `declared` and `connected` are separate on purpose: an agent can declare\n * HubSpot and have nobody connected, which is the state worth showing on a\n * setup checklist. */\nexport interface ConnectionStatus {\n\t/** The connection row's id, which `disconnect()` revokes by. */\n\tid?: string;\n\tprovider: string;\n\t/** 'connected' once the account is attached; anything else means it is not\n\t * usable yet. `connected` below is the boolean form of this. */\n\tstatus?: string;\n\t/** The conversation this account belongs to, or null for the agent's shared\n\t * account. `scope` and `suppliedBy` are both derived from it. */\n\tconversationId?: string | null;\n\t/** The agent this account belongs to. */\n\tagentId?: string | null;\n\t/** One of YOUR end users, when the account is theirs. Their id, from your\n\t * database, never a workspace member's. Null on a shared account. */\n\tendUserId?: string | null;\n\t/** Whether the AGENT DECLARES it needs this provider, and whether it is\n\t * mandatory.\n\t *\n\t * OPTIONAL because only one of the two endpoints can answer. `agent\n\t * .connections()` reads the MERGED view (`/v1/agents/<id>/connections`),\n\t * where the server has joined the agent's declarations against the live\n\t * rows, and both are real there. `ws.connections.list()` and\n\t * `endUser(id).connections.list()` read raw rows (`/v1/connections`), which\n\t * know nothing about any agent's configuration, so both are absent.\n\t *\n\t * They used to be declared as required booleans, which meant the raw-row\n\t * paths had to invent them. They were filled with the constants `true` and\n\t * `false`, so every row claimed to be declared and none claimed to be\n\t * required. Absent is the honest answer, and undefined reads as \"this\n\t * endpoint cannot tell you\" rather than as \"no\". */\n\tdeclared?: boolean;\n\trequired?: boolean;\n\t/** What the agent DECLARED it needs. Whose account this actually is, is\n\t * `owner`, and the two are not the same question. */\n\tscope?: Scope;\n\t/** Whose account this is. Derived from which of the three id fields above\n\t * is set, so it is correct even on a server that does not send it. */\n\towner?: CredentialOwner;\n\tconnected: boolean;\n\t/** Which of the three scopes supplied the account. 'endUser' is one of YOUR\n\t * users, whose account outlives any single conversation. */\n\tsuppliedBy?: 'conversation' | 'agent' | 'endUser' | 'workspace';\n\taccountLabel?: string;\n\terror?: string;\n}\n\n/** Something a conversation is still missing before it can work. */\nexport interface Requirement {\n\tkind: 'secret' | 'connection';\n\tname: string;\n\trequired: boolean;\n}\n\nexport interface AgentConversationData {\n\tid: string;\n\tagentId: string;\n\tversion: number;\n\tstartedAt: string;\n\ttitle?: string;\n\t/** True when nothing REQUIRED is outstanding. An optional gap leaves this\n\t * true, because the conversation genuinely does work without it. */\n\tready: boolean;\n\trequires: Requirement[];\n}\n\nexport interface ProjectConversationData {\n\tid: string;\n\tprojectId: string;\n\tstartedAt: string;\n\ttitle?: string;\n\tready: boolean;\n\trequires: Requirement[];\n}\n\n/** An AGENT on a project, not a person.\n *\n * Called `ProjectAgent` and not `Member`, which is what it was. `Member` is\n * the word this product uses for a HUMAN on the account, so a public type of\n * that name holding an `agentId` was the same trap as `userId` meaning two\n * populations: `import { Member }` handed you the agent one, and the human one\n * had to carry a prefix to get out of its way.\n *\n * The people are `WorkspaceMember`, from `ws.members()`. Nothing here is a\n * person. */\nexport interface ProjectAgent {\n\tagentId: string;\n\tname: string;\n\taddedAt: string;\n}\n\nexport interface ProjectData {\n\tid: string;\n\tname: string;\n\tgoal: string | null;\n\tmanagerInstructions: string | null;\n\tgrade: Grade;\n\tstatus: 'active' | 'archived';\n\tagents: ProjectAgent[];\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface Step {\n\tid: string;\n\tlabel: string;\n\tkind: 'read' | 'write' | 'edit' | 'search' | 'shell' | 'web' | 'data' | 'task' | 'plan' | 'other';\n\tstatus: 'active' | 'done' | 'error';\n}\n\nexport type TurnEvent =\n\t| { type: 'start'; turnId: string }\n\t/** Carries the WHOLE step list every time, not a change to it. Replace what\n\t * you are showing rather than merging; that is what makes reconnecting\n\t * safe. */\n\t| { type: 'progress'; steps: Step[] }\n\t| { type: 'tool'; name: string; state: 'start' | 'end'; ok?: boolean }\n\t/** A fragment, not a line. Append; do not assemble. */\n\t| { type: 'text'; text: string }\n\t| { type: 'file'; path: string }\n\t| { type: 'done'; reply: string }\n\t| { type: 'error'; message: string; retryable: boolean };\n\nexport interface Message {\n\trole: 'user' | 'assistant';\n\tcontent: string;\n\tat: string;\n}\n\n/** An image goes to the model as an image. Anything else is written into the\n * conversation's drive before the turn starts, so the agent can parse it,\n * compute over it, and still have it next turn. */\nexport interface Attachment {\n\tkind: 'image' | 'file';\n\tname?: string;\n\tmimeType: string;\n\t/** base64, without a data: prefix. */\n\tdata: string;\n}\n\n/** A knowledge base: a named folder of files at WORKSPACE scope that an agent\n * configuration attaches by key. */\nexport interface KnowledgeBaseData {\n\t/** Workspace-unique. This is what an agent configuration stores, and it is\n\t * not editable, because renaming it would move every attachment silently. */\n\tkey: string;\n\tname: string;\n\tdescription?: string;\n\t/** Counted when read, so it never disagrees with the files it counts. */\n\tfileCount: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\n/** One file in a knowledge base. PATH, not a flat name: a base is a folder, and\n * the session mount reproduces the shape verbatim. */\n/**\n * Whether an agent can read a file in a knowledge base.\n *\n * Text and markdown are readable as they stand. A PDF, a scan or a spreadsheet\n * has to be turned into text first, which happens when the file is uploaded\n * rather than when a question is asked, so an agent never waits on it.\n *\n * 'readable' an agent can read this now\n * 'processing' being prepared, check back shortly\n * 'unreadable' cannot be read, and `reason` says why\n */\nexport type KnowledgeFileStatus = 'readable' | 'processing' | 'unreadable';\n\nexport interface KnowledgeFileEntry {\n\tpath: string;\n\tmimeType: string;\n\tsize: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\t/** Whether an agent can read this file. */\n\tstatus: KnowledgeFileStatus;\n\t/** Why it cannot be read, as a sentence you could show someone.\n\t *\n\t * The same text reaches the agent in place of the file, so a developer\n\t * reading this and a model reading the base get one explanation rather than\n\t * two that can drift apart. Absent unless `status` is 'unreadable'. */\n\treason?: string;\n}\n\nexport interface FileEntry {\n\tpath: string;\n\tkind: 'file' | 'directory';\n\tmimeType: string;\n\tsize: number;\n\tmodifiedAt: string;\n}\n\n/** Fields accepted when creating or updating an agent. Everything is optional on\n * an update: what you do not send is inherited from the current version, which\n * is the whole reason updating is safe. */\nexport interface AgentFields {\n\tid?: string;\n\tname?: string;\n\tinstructions?: string;\n\tgreeting?: string;\n\tgrade?: Grade;\n\tfilesystem?: Filesystem;\n\t/**\n\t * Knowledge bases this agent reads, by workspace-unique key.\n\t *\n\t * Mounted READ-ONLY at `/knowledge/<key>/` when a session starts, so the\n\t * agent finds things with `ls`, `grep` and `cat` rather than a retrieval API.\n\t *\n\t * Which bases an agent reads is part of its CONFIGURATION, so changing this\n\t * publishes a version you can diff and roll back. The CONTENTS of a base are\n\t * not versioned: correcting a file changes what every attached agent sees on\n\t * its next turn, which is the same by-reference promise `agent.knowledge`\n\t * already makes.\n\t *\n\t * A key that names no base is refused when the configuration is saved, and\n\t * deleting a base is refused while any agent attaches it, so an attachment\n\t * that was valid stays valid.\n\t */\n\tknowledgeBases?: string[];\n\ttools?: string[];\n\tworkflows?: Workflow[];\n\tconnections?: ConnectionRequirement[];\n\tsecrets?: SecretRequirement[];\n\tparameters?: Parameter[];\n\ttriggers?: Trigger[];\n\tmcpServers?: McpServer[];\n\thttpTools?: HttpTool[];\n}\n\nexport interface ProjectFields {\n\tname?: string;\n\tgoal?: string;\n\tmanagerInstructions?: string;\n\tgrade?: Grade;\n\tagents?: string[];\n}\n\n/** Values supplied when a conversation starts: the credentials its agent\n * declared at 'conversation' scope, plus any parameters. */\nexport interface Bindings {\n\tsecrets?: Record<string, string>;\n\tconnections?: Record<string, string>;\n}\n\n/** One markdown file read off disk: a workflow, a subagent, an imported skill.\n *\n * `when` is OPTIONAL here and required at publish. A file on disk has no `when`\n * until someone writes one, and refusing to read it would be the wrong place to\n * say so; the API says it at publish, with a message naming the file. */\nexport interface SpecFile {\n\tname: string;\n\tcontent: string;\n\twhen?: string;\n}\n\n/** What a loaded agent directory produces: the fields `ws.agents.create()` takes,\n * plus the id the caller chose and the few legacy names the loader still reads\n * off an existing directory. */\nexport interface SpecInput extends Omit<AgentFields, 'workflows'> {\n\tkey: string;\n\tworkflows?: SpecFile[];\n\tskills?: SpecFile[];\n\tsubagents?: SpecFile[];\n\twidgets?: { name: string; [k: string]: unknown }[];\n\tsandbox?: Filesystem;\n\trole?: string;\n\t/** Retired in favour of `tools: ['web_search']`, still read off an existing\n\t * agent directory so one written before the rename still loads. */\n\twebSearch?: boolean;\n}\n\n/** A PERSON with access to this workspace: your colleague, on a seat.\n *\n * NOT one of your end users, and the difference is the sharpest naming trap in\n * this SDK. A workspace member signs in here, we issued their id, and we\n * authenticate it. A `EndUser` is the person YOUR application serves, never\n * signs in here, and carries an id from YOUR database that we store verbatim\n * and never check.\n *\n * `userId` below is therefore a PLATFORM id and is not interchangeable with\n * `agent.start({ endUserId })`. Both fields were once spelled `userId`, which\n * made passing one where the other belonged a call that could not fail: a\n * end-user id is never validated against anything, so it silently labelled a\n * conversation with a colleague's internal id. The two names now differ.\n *\n * Named WorkspaceMember rather than Member because a public `Member` type used\n * to mean an AGENT attached to a project. That one is now `ProjectAgent`, so\n * the prefix here is no longer load-bearing; it stays because it says which of\n * the two populations this is at every call site, and that is worth four\n * characters.\n *\n * Read-only, like everything else on the account surface: the SDK can see who\n * is on the account and what they may do, and cannot add, remove or re-role\n * anyone. Those are decisions a person makes while signed in, not something a\n * key should be able to do on their behalf. */\nexport interface WorkspaceMember {\n\t/** A PLATFORM user id (`usr_...`), issued by us. Never pass this to\n\t * `agent.start({ endUserId })` or `ws.endUser()`. */\n\tuserId: string;\n\temail: string;\n\tname: string | null;\n\t/** 'owner' is the account's creator; 'admin' can manage it; 'member' cannot. */\n\trole: 'owner' | 'admin' | 'member';\n\t/** Unix seconds. */\n\tjoinedAt: number;\n}\n\n/** What is left to spend.\n *\n * `credits` is the balance in credits, where one credit is one US dollar. The\n * turn counts are the same balance expressed in the unit people actually\n * reason with, because \"12.5 credits\" answers nothing on its own. */\nexport interface Credits {\n\tcredits: number;\n\tstandardTurnsRemaining: number;\n\tpremiumTurnsRemaining: number;\n}\n\n/** The seat plan this workspace is on.\n *\n * Seats are a SEPARATE purchase from credits: a plan says how many people may\n * share the account, credits pay for the work their agents do. Being on the\n * free plan does not make turns free. */\nexport interface SeatPlan {\n\tkey: string;\n\tlabel: string;\n\t/** People allowed. `null` means negotiated, which is NOT unlimited and\n\t * should never be shown to anyone as a number. */\n\tseats: number | null;\n\t/** Monthly price in US cents. `null` means there is no published price and\n\t * the answer is a conversation, which is the opposite fact from 0 (free). */\n\tusdCentsPerMonth: number | null;\n}\n\n/** A stored secret, WITHOUT its value.\n *\n * The value is never returned by any read. It is written once, encrypted at\n * rest, and substituted into an HTTP tool's url, headers or body server-side at\n * call time, so it is never part of anything the model can see or repeat. To\n * change one, write it again; to be sure of one, replace it. */\nexport interface SecretMeta {\n\tid: string;\n\tname: string;\n\tdescription?: string | null;\n\t/** The agent this secret is scoped to, or null when the whole workspace\n\t * reads it. An agent-scoped secret of the same name wins. */\n\tagentId?: string | null;\n\t/** The end user this secret belongs to, or null when it is the workspace's.\n\t * One of YOUR user ids, stored verbatim: we never resolve or authenticate\n\t * it. A secret with one can only be attached to a conversation launched for\n\t * that same person. */\n\tendUserId?: string | null;\n\tcreatedAt?: string;\n\tupdatedAt?: string;\n\tlastUsedAt?: string | null;\n}\n"]}
|
package/version.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SDK_VERSION = "0.26.0";
|
package/version.js
ADDED
package/version.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,4EAA4E;AAC5E,eAAe;AACf,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC","sourcesContent":["// GENERATED by scripts/regen-version.mjs. Do not edit: run `bun run regen:version`\n// after bumping the version in package.json. tests/version.test.ts fails if\n// this drifts.\nexport const SDK_VERSION = '0.26.0';\n"]}
|
package/workflows.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Workflow } from './types.ts';
|
|
2
|
+
/** The bits of `node:fs` this needs, so it can be faked in a test and so the
|
|
3
|
+
* module does not break a browser bundle merely by being imported. */
|
|
4
|
+
interface FsLike {
|
|
5
|
+
readdirSync(dir: string): string[];
|
|
6
|
+
readFileSync(path: string, encoding: 'utf8'): string;
|
|
7
|
+
statSync(path: string): {
|
|
8
|
+
isDirectory(): boolean;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parse one SKILL.md-shaped document.
|
|
13
|
+
*
|
|
14
|
+
* Exported because it is the part worth testing: the frontmatter, not the
|
|
15
|
+
* directory walk.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseWorkflow(source: string, fallbackName: string): Workflow;
|
|
18
|
+
/**
|
|
19
|
+
* Read every workflow under `dir`.
|
|
20
|
+
*
|
|
21
|
+
* Handles both layouts without being told which: a flat directory of
|
|
22
|
+
* `*.md` files, and Claude Code's `<name>/SKILL.md` directories. Requiring the
|
|
23
|
+
* caller to say which one they have would be a question they should not have to
|
|
24
|
+
* answer about their own folder.
|
|
25
|
+
*
|
|
26
|
+
* Synchronous, because this runs at configuration time in a script, and an async
|
|
27
|
+
* version would infect every call site that builds an agent literal.
|
|
28
|
+
*
|
|
29
|
+
* NODE AND BUN ONLY. `node:fs` is a static import here, so a browser bundle that
|
|
30
|
+
* reaches this module will fail at build time rather than at run time. That is
|
|
31
|
+
* the right trade for a helper that exists to read a folder off disk: in a
|
|
32
|
+
* browser there is no folder, and the answer is to pass `workflows` directly.
|
|
33
|
+
* The `fs` parameter exists so this is testable without touching a disk.
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadWorkflows(dir: string, fs?: FsLike): Workflow[];
|
|
36
|
+
export {};
|