@cerebro-labs/agentnava-sdk 0.26.0 → 0.46.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.
- package/README.md +212 -23
- package/account.d.ts +154 -0
- package/account.js +219 -0
- package/account.js.map +1 -0
- package/agent.d.ts +229 -87
- package/agent.js +310 -125
- package/agent.js.map +1 -1
- package/apps.generated.d.ts +154 -0
- package/apps.generated.js +152 -0
- package/apps.generated.js.map +1 -0
- package/attach.d.ts +25 -26
- package/attach.js +53 -66
- package/attach.js.map +1 -1
- package/connection.d.ts +79 -82
- package/connection.js +67 -134
- package/connection.js.map +1 -1
- package/conversation.d.ts +55 -35
- package/conversation.js +70 -50
- package/conversation.js.map +1 -1
- package/database.d.ts +53 -0
- package/database.js +69 -0
- package/database.js.map +1 -0
- package/end-user.d.ts +58 -47
- package/end-user.js +62 -72
- package/end-user.js.map +1 -1
- package/errors.d.ts +2 -1
- package/errors.js +2 -1
- package/errors.js.map +1 -1
- package/http.d.ts +5 -0
- package/http.js +109 -8
- package/http.js.map +1 -1
- package/import-tools.d.ts +54 -33
- package/import-tools.js +526 -124
- package/import-tools.js.map +1 -1
- package/index.d.ts +5 -2
- package/index.js +9 -2
- package/index.js.map +1 -1
- package/load-agent-dir.js +28 -5
- package/load-agent-dir.js.map +1 -1
- package/package.json +5 -1
- package/secret.d.ts +1 -1
- package/secret.js +1 -1
- package/secret.js.map +1 -1
- package/templates-inline.js +1 -1
- package/templates-inline.js.map +1 -1
- package/turn.d.ts +1 -1
- package/turn.js +77 -29
- package/turn.js.map +1 -1
- package/types.d.ts +462 -83
- package/types.js.map +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
- package/workflows.d.ts +10 -0
- package/workflows.js +16 -0
- package/workflows.js.map +1 -1
- package/workspace.d.ts +80 -62
- package/workspace.js +92 -75
- package/workspace.js.map +1 -1
- package/connection-status.d.ts +0 -28
- package/connection-status.js +0 -61
- package/connection-status.js.map +0 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ const agent = await ws.agents.create({
|
|
|
19
19
|
instructions: 'Decide whether a late delivery qualifies for a refund.',
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
const conversation = await agent.start();
|
|
22
|
+
const conversation = await agent.start({ endUserId: 'u_ananya' });
|
|
23
23
|
console.log(await conversation.ask('Order 4471 arrived nine days late.'));
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -30,30 +30,112 @@ only the things that are easy to get wrong.
|
|
|
30
30
|
|
|
31
31
|
## Connecting an account, end to end
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
An **account** is one saved sign-in to one app. A **connection** is an entry in
|
|
34
|
+
an agent's setup that names one, so the agent uses exactly that account and never
|
|
35
|
+
another. `ws.accounts.connect()` gives you a URL for a person to visit, and
|
|
36
|
+
`waitUntilConnected()` tells you when they are done.
|
|
36
37
|
|
|
37
38
|
```ts
|
|
38
|
-
|
|
39
|
+
import { App, Connection } from '@cerebro-labs/agentnava-sdk';
|
|
40
|
+
|
|
41
|
+
const { account, url } = await ws.accounts.connect({ app: App.GMAIL, nickname: 'Support inbox' });
|
|
39
42
|
showToUser(url); // popup, redirect, QR code, your choice
|
|
40
|
-
await
|
|
43
|
+
await account.waitUntilConnected(); // resolves once they have signed in
|
|
44
|
+
await agent.connections.add(Connection.creatorAccount(App.GMAIL, account));
|
|
41
45
|
```
|
|
42
46
|
|
|
43
47
|
How you show the URL is yours: this SDK authenticates with an API key, so it
|
|
44
48
|
runs on your server, and the browser half is your application's.
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
On timeout it throws `account_not_connected` and the message names the account
|
|
51
|
+
and where it stands. It is safe to call again; someone who has not finished
|
|
52
|
+
signing in may just need longer. After `account.reconnect()` it waits for the
|
|
53
|
+
new sign-in too: the account keeps working meanwhile, with `reconnecting` true.
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
For an app each of YOUR users signs in to themselves, the connection uses the
|
|
56
|
+
end user's account. Each person signs in once, it is remembered for them, and
|
|
57
|
+
nothing is attached per conversation:
|
|
52
58
|
|
|
53
59
|
```ts
|
|
54
|
-
await
|
|
60
|
+
await agent.connections.add(Connection.endUserAccount(App.GOOGLE_CALENDAR));
|
|
61
|
+
|
|
62
|
+
const conversation = await agent.start({ endUserId: user.id });
|
|
63
|
+
for (const { app, url } of await conversation.accountsNeeded()) {
|
|
64
|
+
showToUser(app, url); // the ones this person has not connected yet
|
|
65
|
+
}
|
|
55
66
|
```
|
|
56
67
|
|
|
68
|
+
An end user's account is an ordinary `Account`: `ws.endUser(id).accounts.connect()`
|
|
69
|
+
returns one, and `waitUntilConnected()`, `ws.account(id)` and every other account
|
|
70
|
+
call work on it the same way. `account.data.owner` says whose it is.
|
|
71
|
+
|
|
72
|
+
The builders make plain objects, `{ app, account: '<id>' }` and
|
|
73
|
+
`{ app, endUser: true }`, and either object written by hand is accepted too.
|
|
74
|
+
`App.GMAIL` is the string `'gmail'`, so a plain string works anywhere a constant
|
|
75
|
+
does, including for an app added after your SDK version.
|
|
76
|
+
|
|
77
|
+
## 0.44.0 is a breaking change: apps and accounts
|
|
78
|
+
|
|
79
|
+
Two words now mean two things. An **account** is one saved sign-in to one app.
|
|
80
|
+
A **connection** is an entry in an agent's setup: a name, an app, and whose
|
|
81
|
+
account it uses, built with `Connection.creatorAccount(app, account)` for the
|
|
82
|
+
agent creator's account or `Connection.endUserAccount(app)` for each end user's
|
|
83
|
+
own. A turn uses exactly the account the setup names. Nothing is attached to a
|
|
84
|
+
conversation any more, and nothing falls back to some other account of the same
|
|
85
|
+
app.
|
|
86
|
+
|
|
87
|
+
Upgrade together with the production release that serves the new routes:
|
|
88
|
+
0.44 calls `/v1/accounts`, `/v1/end-users/:id/accounts` and the agent connection
|
|
89
|
+
routes, and the connection routes 0.43 calls are removed in that same release.
|
|
90
|
+
There is no compatibility shim: this package is pre-launch, so the old names
|
|
91
|
+
were removed rather than deprecated.
|
|
92
|
+
|
|
93
|
+
| 0.43 | 0.44 |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `ws.connections.create({ provider })` | `ws.accounts.connect({ app, nickname? })`, returns `{ account, url }` |
|
|
96
|
+
| `ws.connections.create({ provider, agentId })` | `ws.accounts.connect({ app })`, then `agent.connections.add(Connection.creatorAccount(app, account))` |
|
|
97
|
+
| `ws.connections.create({ provider, endUserId })` | `ws.endUser(id).accounts.connect({ app })` |
|
|
98
|
+
| `ws.connections.list()` | `ws.accounts.list({ app? })`, each with `usedBy` |
|
|
99
|
+
| `ws.connections.list({ endUserId })` | `ws.endUser(id).accounts.list()` |
|
|
100
|
+
| `ws.connection(id)` | `ws.account(id)` |
|
|
101
|
+
| `Connection` (class): a signed-in account | `Account` (class). The name `Connection` now builds an entry of an agent's setup |
|
|
102
|
+
| `connection.get()`, returned `ConnectionStatus` | `account.get()`, returns `AccountData`; a missing account throws `account_not_found` |
|
|
103
|
+
| `connection.waitUntilConnected()` | `account.waitUntilConnected()`; times out with `account_not_connected` |
|
|
104
|
+
| `connection.revoke()` | `account.disconnect()`, returns `{ agents }`: the agents that lose it |
|
|
105
|
+
| `connection.provider`, `.connected`, `.owner`, `.endUserId` | `account.app`, `.status`, `.data.owner`, `.data.endUserId` |
|
|
106
|
+
| `connection.url` | the `url` that `connect` returns |
|
|
107
|
+
| none | `account.reconnect()`, `account.rename(nickname)`, `account.check()`, `account.label`, `account.nickname`, `account.usedBy`, `account.data.reconnecting` |
|
|
108
|
+
| `agent.authorize(provider)` | `ws.accounts.connect({ app })`, then `agent.connections.add(Connection.creatorAccount(app, account))` |
|
|
109
|
+
| `agent.attach(connection)` | `agent.connections.add(Connection.creatorAccount(app, account, { name? }))` |
|
|
110
|
+
| none | `agent.connections.add(Connection.endUserAccount(app, { name? }))` |
|
|
111
|
+
| none | `agent.connections.change(name, { account })` or `(name, { endUser: true })` |
|
|
112
|
+
| `agent.disconnect(provider)`, which deleted the shared account | `agent.connections.remove(name)`, which never touches the account |
|
|
113
|
+
| `agent.connections()` | `agent.connections.list()` |
|
|
114
|
+
| `agent.attach(secret)`, which was accepted and did nothing | refused with `not_a_secret`; attach a stored secret to a conversation instead |
|
|
115
|
+
| `agent.start({ attach: connection })` | `agent.start({ endUserId })`: the setup names the accounts |
|
|
116
|
+
| `conversation.authorize(provider)` | `conversation.accountsNeeded()`: the links an end user still has to sign in at |
|
|
117
|
+
| `conversation.attach(connection)` | none: the agent's setup decides |
|
|
118
|
+
| `endUser.connections.create({ provider })` | `endUser.accounts.connect({ app })` |
|
|
119
|
+
| `endUser.connections.list()` | `endUser.accounts.list()` |
|
|
120
|
+
| `endUser.revokeConnections()` | `endUser.accounts.disconnectAll()`, returns `{ disconnected }` |
|
|
121
|
+
| setup `connections: [{ provider, scope: 'fixed', connectionId }]` | `Connection.creatorAccount(app, account, { name? })`, which is `{ name?, app, account: '<id>' }` |
|
|
122
|
+
| setup `connections: [{ provider, scope: 'conversation' }]` | `Connection.endUserAccount(app, { name? })`, which is `{ name?, app, endUser: true }` |
|
|
123
|
+
| type `ConnectionRequirement` | `AgentConnection` (as read back) and `Connection` (as you send it), with `CreatorAccountConnection` and `EndUserAccountConnection` |
|
|
124
|
+
| type `ConnectionStatus`, keyed by `provider` | `ConnectionStatus`, keyed by `name`: `{ name, app, account?, endUser?, status, inDraft, removedInDraft }` |
|
|
125
|
+
| type `CredentialOwner` | removed: `AccountData.owner` is `'workspace'` or `'end-user'` |
|
|
126
|
+
| `Bindings.connections` | removed: `Bindings` carries `secrets` only |
|
|
127
|
+
| none | `App`: every app id as a constant (`App.GMAIL`), and the type `AppId` |
|
|
128
|
+
|
|
129
|
+
New error codes to branch on: `account_not_found`, `connection_name_taken`,
|
|
130
|
+
`account_app_mismatch`, `connection_not_found` (from
|
|
131
|
+
`agent.connections.change`), `account_not_connected` (from
|
|
132
|
+
`waitUntilConnected`), `account_required` (from `Connection.creatorAccount`
|
|
133
|
+
with no account), and `not_a_secret` (an account handed to `attach`).
|
|
134
|
+
|
|
135
|
+
Secrets did not move: `agent.attach({ secrets })`, `conversation.attach(secret)`,
|
|
136
|
+
`conversation.attach({ secrets })` and `agent.start({ attach: secret, secrets })`
|
|
137
|
+
work as before.
|
|
138
|
+
|
|
57
139
|
## 0.2.0 is a breaking change. Upgrade from 0.1.1, do not stay on it.
|
|
58
140
|
|
|
59
141
|
"session" is gone from everything you touch. It survives only in our storage,
|
|
@@ -77,8 +159,8 @@ moved.
|
|
|
77
159
|
|
|
78
160
|
## Holding an object is free
|
|
79
161
|
|
|
80
|
-
`ws.agent(id)`, `
|
|
81
|
-
call**. You get something you can call methods on, and the first request happens
|
|
162
|
+
`ws.agent(id)`, `agent.conversation(id)`, `ws.project(id)` and `ws.account(id)`
|
|
163
|
+
make **no network call**. You get something you can call methods on, and the first request happens
|
|
82
164
|
when you call one.
|
|
83
165
|
|
|
84
166
|
Store the `id`, not the object. A returned object is a handle, not JSON.
|
|
@@ -97,16 +179,58 @@ for await (const e of conversation.ask('...')) { // as it happens
|
|
|
97
179
|
```
|
|
98
180
|
|
|
99
181
|
**The message is sent when you call `ask`**, not when you await or iterate it.
|
|
100
|
-
Calling `ask` twice asks twice.
|
|
182
|
+
Calling `ask` twice generates two submission keys and asks twice. If you need
|
|
183
|
+
to identify an explicit retry after a lost response, save a key before sending:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const idempotencyKey = crypto.randomUUID();
|
|
187
|
+
const reply = await conversation.ask('...', { idempotencyKey });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Reuse that key only with the same content in the same conversation. The API
|
|
191
|
+
refuses an already accepted turn with `turn_already_accepted` instead of running
|
|
192
|
+
it again. A new intentional question needs a new key. Errors include the
|
|
193
|
+
original key and instructions for checking status; a `retryable: false` error
|
|
194
|
+
must not be retried as a fresh submission.
|
|
101
195
|
|
|
102
196
|
You may await the same turn more than once; it is a promise, and you get the same
|
|
103
197
|
answer. You may not iterate it twice, or mix the two, because the events are a
|
|
104
198
|
live stream rather than a stored list and the second consumer would receive
|
|
105
199
|
nothing at all. That throws instead.
|
|
106
200
|
|
|
107
|
-
**The turn is not your request.**
|
|
108
|
-
|
|
109
|
-
|
|
201
|
+
**The turn is not your request.** Once the server accepts it, a dropped
|
|
202
|
+
connection, aborting `AskOptions.signal`, or leaving a `for await` loop early
|
|
203
|
+
stops receiving the response. Accepted work can continue and normal billing
|
|
204
|
+
applies; disconnecting does not cancel server work or automatically refund the
|
|
205
|
+
turn. An already-aborted signal passed to `ask()` prevents submission, so no
|
|
206
|
+
turn is accepted or charged for that call.
|
|
207
|
+
|
|
208
|
+
Use a signal to limit how long your application waits:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const controller = new AbortController();
|
|
212
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
213
|
+
try {
|
|
214
|
+
const reply = await conversation.ask('Summarize the report.', {
|
|
215
|
+
signal: controller.signal,
|
|
216
|
+
});
|
|
217
|
+
save(reply);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!controller.signal.aborted) throw error;
|
|
220
|
+
// We stopped waiting. A turn already accepted by the server can still run.
|
|
221
|
+
} finally {
|
|
222
|
+
clearTimeout(timer);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
After an interruption, read `await conversation.messages()` before another
|
|
227
|
+
`ask()`. The answer may still be in progress, so check history again later
|
|
228
|
+
instead of submitting the same question again.
|
|
229
|
+
|
|
230
|
+
A successful `await` or `done` event requires the server's completion marker.
|
|
231
|
+
If the stream closes early, the SDK reports a nonretryable interruption even
|
|
232
|
+
when reply text already arrived; use the original submission key to check its
|
|
233
|
+
status before starting more work.
|
|
110
234
|
|
|
111
235
|
## Changing an agent publishes a version
|
|
112
236
|
|
|
@@ -150,13 +274,22 @@ await conversation.ask('What are the payment terms?');
|
|
|
150
274
|
Anywhere under the root works. `input/` is a convention here and nothing
|
|
151
275
|
enforces it, because the agent and your application share one namespace.
|
|
152
276
|
|
|
153
|
-
##
|
|
277
|
+
## Whose account an agent uses
|
|
278
|
+
|
|
279
|
+
Each connection in an agent's setup names one account, and a turn uses exactly
|
|
280
|
+
that one:
|
|
281
|
+
|
|
282
|
+
| Built with | Whose | Who signs in |
|
|
283
|
+
|---|---|---|
|
|
284
|
+
| `Connection.creatorAccount(app, account)` | the agent creator's | you or a colleague, once; every conversation uses it |
|
|
285
|
+
| `Connection.endUserAccount(app)` | the end user's | each person, once, remembered for them |
|
|
154
286
|
|
|
155
|
-
|
|
156
|
-
|
|
287
|
+
There is no fallback. An account that is not signed in leaves its connection
|
|
288
|
+
unavailable, and the agent says so rather than reaching for another account of
|
|
289
|
+
the same app.
|
|
157
290
|
|
|
158
|
-
|
|
159
|
-
|
|
291
|
+
Secrets still come in two tiers that do not substitute: `fixed` is set once on
|
|
292
|
+
the agent, `conversation` is supplied per conversation. Check
|
|
160
293
|
`conversation.ready` and `conversation.requires` to see what is outstanding.
|
|
161
294
|
|
|
162
295
|
## Drafting tools from an API you already describe
|
|
@@ -184,6 +317,23 @@ the big one is `description-missing`: none of the three formats contains the
|
|
|
184
317
|
sentence a model reads to pick a tool, and a bad description does not fail, it
|
|
185
318
|
makes the agent call the wrong thing.
|
|
186
319
|
|
|
320
|
+
A rule in an OpenAPI document (a minimum, a length, a pattern, a format, a
|
|
321
|
+
`oneOf`) does not cost you the operation. It is written into the parameter's
|
|
322
|
+
description, `Rules: at least 1; at most 100.`, and your API enforces it as it
|
|
323
|
+
does for any caller; its 400 reaches the agent as a failed result it can
|
|
324
|
+
correct. A document that defines one security scheme and never lists it under
|
|
325
|
+
`security` gets that scheme on every operation, with a `security-choice`
|
|
326
|
+
warning; with several schemes, none is applied and the warning says so.
|
|
327
|
+
|
|
328
|
+
### HTTP tool response limits
|
|
329
|
+
|
|
330
|
+
Declarative HTTP tools retain at most 64 KiB of response data and stop reading
|
|
331
|
+
when overflow is detected. The limit counts UTF-8 bytes, not characters. Oversized responses
|
|
332
|
+
return `ok: false`, `truncated: true`, and a `response_too_large` error body
|
|
333
|
+
with pagination/filtering guidance; the HTTP status remains the upstream
|
|
334
|
+
status. Partial JSON is never presented as a complete result. Use the API's
|
|
335
|
+
filters and pagination to retrieve a large dataset in bounded pages.
|
|
336
|
+
|
|
187
337
|
## Errors
|
|
188
338
|
|
|
189
339
|
Every failure throws `AgentNavaError`.
|
|
@@ -220,6 +370,26 @@ Runs anywhere `fetch` exists: Node, Bun, Deno, Cloudflare Workers, the browser.
|
|
|
220
370
|
`loadWorkflows` is the one exception; it reads a directory off disk and so needs
|
|
221
371
|
Node or Bun.
|
|
222
372
|
|
|
373
|
+
## Pointing at a different deployment
|
|
374
|
+
|
|
375
|
+
The SDK talks to `https://api.agentnava.com` unless you say otherwise. Two ways
|
|
376
|
+
to change it, and the explicit option wins over the environment variable.
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
// a dedicated enterprise endpoint
|
|
380
|
+
const ws = AgentNava.getWorkspace({ apiKey, baseURL: 'https://acme.agentnava.example' });
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
# staging, which is where a test key works
|
|
385
|
+
AGENTNAVA_BASE_URL=https://api-staging.agentnava.com
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**A staging key against the production URL fails with `invalid api key`**, which
|
|
389
|
+
reads like a bad key rather than the wrong host. If you were given a test key,
|
|
390
|
+
set the staging URL above. A trailing slash is fine, and a base with a path
|
|
391
|
+
prefix works too, for an API gateway sitting in front of us.
|
|
392
|
+
|
|
223
393
|
## Licence
|
|
224
394
|
|
|
225
395
|
Apache License 2.0. See `LICENSE`, and `NOTICE` for the attribution that
|
|
@@ -228,3 +398,22 @@ section 4(d) asks you to carry along.
|
|
|
228
398
|
Free to use, modify and redistribute, including commercially, with an
|
|
229
399
|
explicit patent grant. Section 6 grants no rights to the names: "AgentNava"
|
|
230
400
|
and "Cerebro Labs" remain trademarks of Cerebro Labs, Inc.
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
### Where an agent is live (0.41)
|
|
404
|
+
|
|
405
|
+
`agent.get()` and `ws.agents.list()` carry a read-only `status`: where the agent is live, by the rule the API runs it by, and in `needs` any required account or secret not set up yet (it still runs; the tools that use them fail until they are).
|
|
406
|
+
|
|
407
|
+
### Enterprise HTTP contracts (0.34)
|
|
408
|
+
|
|
409
|
+
Requires the corresponding API and runtime update. Parameter types include object and array, nested properties/items, per-property required, nullable, integer and additionalProperties. Use choices for primitive enums. Supply native JSON values to paramsJson; an object body is not a JSON-encoded string.
|
|
410
|
+
|
|
411
|
+
The OpenAPI importer respects operation security, document security, and security:[] (public). When a document defines exactly one supported scheme and no `security` array anywhere names one, that scheme applies to every operation that does not say `security: []`, with a `security-choice` warning; with several schemes, or one the importer cannot set up (OAuth, cookie), none is applied and the warning says the tools send no credential. Bearer, Basic and header/query API keys are supported. It preserves inherited parameters, resolves acyclic local schema references, and URL-encodes path/query values. Omitted optional query/header values and an optional whole body are not sent; missing required values still fail before HTTP. Secrets in query values use secretsEncoded.
|
|
412
|
+
|
|
413
|
+
The `secretName` override can represent only one credential per operation. The importer checks each security alternative together with the operation's credential header parameters and selects the first compatible alternative. If none can use a single credential, the importer skips the operation with a warning instead of reusing the same secret for independent credentials. Omit the override to generate independent secret names, or configure the operation explicitly.
|
|
414
|
+
|
|
415
|
+
Review import warnings. Schema rules never skip an operation: bounds, lengths, patterns, string formats and item or field counts are written into the parameter's description, and the API being called enforces them. `oneOf`/`anyOf` become one parameter that admits every alternative, with the alternatives described (`One of: Card (with type, token); BankTransfer (with type, iban).`); "X or null" becomes a nullable X, and a map (`additionalProperties` holding a schema) stays open to any key. When the alternatives cannot share one shape (a list or an object), the first is kept and an `unsupported-body` warning names the field. `allOf` members merge, including a rule-only member beside a type. Choosing among security alternatives produces a warning. What still skips an operation, with an `unsupported-operation` warning: a contradictory `allOf`, tuple arrays (`prefixItems`), recursive or external references, a schema with no type, structured query serialization, cookie authentication and OAuth acquisition/refresh. Schemas cannot declare a property named `__proto__`, which the Workers schema adapter cannot preserve.
|
|
416
|
+
|
|
417
|
+
Keywords beside a schema `$ref` apply together with the referenced schema, as OpenAPI 3.1 defines, at every step of a reference chain: a description replaces the referenced one, a rule is described, an enum narrows the choices. Response-only properties marked `readOnly: true` are still omitted before importing their request schemas.
|
|
418
|
+
|
|
419
|
+
All three execution paths share one HTTP executor. Results include lower-cased Link, Retry-After, request ID, ETag, Last-Modified, Content-Type and rate-limit headers. Credential-bearing headers are excluded and known secret values in metadata are redacted. HTTP errors and oversized responses have ok:false at the outer tool envelope too. The response byte limit remains 64 KiB: filter or paginate larger records.
|
package/account.d.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Http } from './http.ts';
|
|
2
|
+
import type { AccountData, AccountStatus, UsedBy } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* One saved sign-in to one app, that you hold and act on.
|
|
5
|
+
*
|
|
6
|
+
* AN ACCOUNT IS NOT A CONNECTION, and the two words used to be one, which is
|
|
7
|
+
* how an agent came to own a Gmail login that nothing else could see. An
|
|
8
|
+
* ACCOUNT is a sign-in: Gmail, signed in as support@example.com. A CONNECTION is
|
|
9
|
+
* an entry in an agent's setup that names one: "this agent uses Gmail, through
|
|
10
|
+
* that account". Many agents can name one account, and reconnecting it once
|
|
11
|
+
* fixes all of them.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const { account, url } = await ws.accounts.connect({ app: App.GMAIL, nickname: 'Support inbox' });
|
|
15
|
+
* showToUser(url); // they sign in on Google's own page
|
|
16
|
+
* await account.waitUntilConnected();
|
|
17
|
+
* await agent.connections.add(Connection.creatorAccount(App.GMAIL, account));
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Nobody handles a token, you included: the person signs in on the app's own
|
|
21
|
+
* page, and this object says who they signed in as, never how.
|
|
22
|
+
*
|
|
23
|
+
* ONE KIND OF OBJECT FOR BOTH OWNERS. The agent creator's account and one of
|
|
24
|
+
* your end users' are the same class, reached by the same routes: every method
|
|
25
|
+
* here works on any account in the workspace, and `data.owner` says whose it
|
|
26
|
+
* is. So `ws.account(id).waitUntilConnected()` is how you wait for an end user
|
|
27
|
+
* to finish signing in, too.
|
|
28
|
+
*
|
|
29
|
+
* A CLASS, like `Database` and `Secret`: you hold it and act on it, rather than
|
|
30
|
+
* threading an id through calls. Holding one makes no request. `ws.account(id)`
|
|
31
|
+
* is free and the first call on it is the first round trip.
|
|
32
|
+
*/
|
|
33
|
+
export declare class Account {
|
|
34
|
+
#private;
|
|
35
|
+
readonly id: string;
|
|
36
|
+
private snapshot?;
|
|
37
|
+
constructor(http: Http, id: string, snapshot?: AccountData);
|
|
38
|
+
private get base();
|
|
39
|
+
/** The app's catalog id: `'gmail'`, `'hubspot'`. Named for what the person
|
|
40
|
+
* signing in recognises, never for whoever we buy the sign-in from. */
|
|
41
|
+
get app(): string | undefined;
|
|
42
|
+
/** Who it is signed in as: the email or username the app reports. Null until
|
|
43
|
+
* somebody has finished signing in. */
|
|
44
|
+
get label(): string | null | undefined;
|
|
45
|
+
/** Your own name for it, such as `'Support inbox'`. */
|
|
46
|
+
get nickname(): string | null | undefined;
|
|
47
|
+
/** Where the sign-in stands, from the last read: `'pending'`, `'connected'`,
|
|
48
|
+
* `'needs_sign_in'` or `'error'`.
|
|
49
|
+
*
|
|
50
|
+
* Reads the snapshot rather than the network. Undefined means this handle
|
|
51
|
+
* has not read yet, NOT that the account is disconnected: call `get()`. */
|
|
52
|
+
get status(): AccountStatus | undefined;
|
|
53
|
+
/** The agents whose setup names this account, from the last read. The
|
|
54
|
+
* question to ask before disconnecting it: who loses it? */
|
|
55
|
+
get usedBy(): UsedBy[] | undefined;
|
|
56
|
+
/** The last-known record, from whatever call produced this object. */
|
|
57
|
+
get data(): AccountData | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* An account goes over the wire as its id, the way a `Date` goes as its ISO
|
|
60
|
+
* string: `JSON.stringify({ account })` sends `"acc_..."`.
|
|
61
|
+
*
|
|
62
|
+
* That is what lets a connection written as a plain object,
|
|
63
|
+
* `{ app, account }`, take the Account you are holding in
|
|
64
|
+
* `ws.agents.create()` and `agent.update()`. It also matches the rule for
|
|
65
|
+
* every handle in this SDK: store the id, not the object.
|
|
66
|
+
*/
|
|
67
|
+
toJSON(): string;
|
|
68
|
+
/**
|
|
69
|
+
* Read it back.
|
|
70
|
+
*
|
|
71
|
+
* A MISSING ACCOUNT THROWS (`account_not_found`, 404), and never comes back
|
|
72
|
+
* as an empty record. An account that does not exist and one that is not
|
|
73
|
+
* signed in yet are different answers. The connection this replaced once
|
|
74
|
+
* answered a miss with a made-up blank, so storing an id and reading it back
|
|
75
|
+
* gave you something that did not know what it was.
|
|
76
|
+
*/
|
|
77
|
+
get(): Promise<AccountData>;
|
|
78
|
+
/**
|
|
79
|
+
* Wait until the person has finished signing in.
|
|
80
|
+
*
|
|
81
|
+
* The companion to `ws.accounts.connect()`, `ws.endUser(id).accounts
|
|
82
|
+
* .connect()` and `reconnect()`. Those hand you a url; this tells you when
|
|
83
|
+
* the person is done, so you do not write a polling loop or guess which
|
|
84
|
+
* field to believe.
|
|
85
|
+
*
|
|
86
|
+
* DONE MEANS CONNECTED AND NOT RECONNECTING. During a reconnect the account
|
|
87
|
+
* keeps working (`status` stays `'connected'`) while `reconnecting` is true,
|
|
88
|
+
* so a check on `status` alone would return before the person had signed in
|
|
89
|
+
* again. This waits for both.
|
|
90
|
+
*
|
|
91
|
+
* THROWS on the deadline (`account_not_connected`), naming the account and
|
|
92
|
+
* where it stands, rather than returning an account that is still pending.
|
|
93
|
+
* Safe to call again: somebody who has not finished may just need longer.
|
|
94
|
+
*
|
|
95
|
+
* Polls rather than subscribing: there is a person in the middle, so a few
|
|
96
|
+
* seconds costs nothing, and it works from any runtime without a socket.
|
|
97
|
+
*/
|
|
98
|
+
waitUntilConnected(options?: {
|
|
99
|
+
timeoutMs?: number;
|
|
100
|
+
intervalMs?: number;
|
|
101
|
+
}): Promise<Account>;
|
|
102
|
+
/**
|
|
103
|
+
* Send the person to sign in again, and get the link.
|
|
104
|
+
*
|
|
105
|
+
* What to call when an account `needs_sign_in`. Every agent that names it
|
|
106
|
+
* follows, so reconnecting once fixes all of them. The sign-in that worked is
|
|
107
|
+
* kept until the new one succeeds, so an abandoned link breaks nothing:
|
|
108
|
+
* meanwhile `data.reconnecting` is true, and `waitUntilConnected()` waits
|
|
109
|
+
* for it to clear.
|
|
110
|
+
*
|
|
111
|
+
* If the person signs in as SOMEBODY ELSE, that becomes a NEW account and
|
|
112
|
+
* this one is left as it was, so no agent switches mailbox without anyone
|
|
113
|
+
* deciding it should. List accounts to find the new one, then change the
|
|
114
|
+
* connections that should use it.
|
|
115
|
+
*/
|
|
116
|
+
reconnect(): Promise<{
|
|
117
|
+
url: string;
|
|
118
|
+
}>;
|
|
119
|
+
/** Give it your own name, such as `'Support inbox'`, shown beside who it is
|
|
120
|
+
* signed in as. The app and the sign-in do not change. */
|
|
121
|
+
rename(nickname: string): Promise<Account>;
|
|
122
|
+
/** Ask the app whether the sign-in still works, and read the answer. A
|
|
123
|
+
* revoked sign-in comes back as `needs_sign_in`, not as an exception: it is
|
|
124
|
+
* the answer this call exists to get. */
|
|
125
|
+
check(): Promise<AccountData>;
|
|
126
|
+
/**
|
|
127
|
+
* Sign it out at the app and remove it here.
|
|
128
|
+
*
|
|
129
|
+
* ALWAYS ALLOWED, unlike deleting a database that an agent still names. It
|
|
130
|
+
* returns the agents whose setup names the account, so you can tell whoever
|
|
131
|
+
* owns them: from their next turn those connections report that the account
|
|
132
|
+
* was disconnected. Nothing switches to another account in its place.
|
|
133
|
+
*
|
|
134
|
+
* Removing a connection from an agent is the other thing, and it never
|
|
135
|
+
* touches the account: `agent.connections.remove(name)`.
|
|
136
|
+
*/
|
|
137
|
+
disconnect(): Promise<{
|
|
138
|
+
agents: UsedBy[];
|
|
139
|
+
}>;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The account a response carries, or a thrown error. Never a blank.
|
|
143
|
+
*
|
|
144
|
+
* A 404 already throws in the transport, with the API's `account_not_found`.
|
|
145
|
+
* This covers the other way to end up with nothing: a 200 whose body has no
|
|
146
|
+
* account in it, which would otherwise become a snapshot of `undefined` that
|
|
147
|
+
* reads, at every call site, exactly like an account nobody has read yet.
|
|
148
|
+
*/
|
|
149
|
+
export declare function accountIn(res: {
|
|
150
|
+
account?: AccountData;
|
|
151
|
+
} | undefined, path: string): AccountData;
|
|
152
|
+
/** The id a connection sends for its account: an `Account` by its id, an id
|
|
153
|
+
* as it is. */
|
|
154
|
+
export declare function accountIdOf(account: Account | string): string;
|