@cerebro-labs/agentnava-sdk 0.75.1 → 0.80.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/README.md +56 -2
- package/account.d.ts +1 -1
- package/account.js +1 -1
- package/account.js.map +1 -1
- package/agent.d.ts +49 -5
- package/agent.js +45 -2
- package/agent.js.map +1 -1
- package/end-user.d.ts +83 -32
- package/end-user.js +101 -38
- package/end-user.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/react.d.ts +3 -1
- package/react.js +18 -2
- package/react.js.map +1 -1
- package/secret.d.ts +1 -1
- package/secret.js +1 -1
- package/secret.js.map +1 -1
- package/types.d.ts +80 -1
- package/types.js.map +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
- package/workspace.d.ts +4 -56
- package/workspace.js +7 -46
- package/workspace.js.map +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ for (const { app, url } of await conversation.accountsNeeded()) {
|
|
|
65
65
|
}
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
An end user's account is an ordinary `Account`: `
|
|
68
|
+
An end user's account is an ordinary `Account`: `agent.endUser(id).accounts.connect()`
|
|
69
69
|
returns one, and `waitUntilConnected()`, `ws.account(id)` and every other account
|
|
70
70
|
call work on it the same way. `account.data.owner` says whose it is.
|
|
71
71
|
|
|
@@ -74,6 +74,60 @@ The builders make plain objects, `{ app, account: '<id>' }` and
|
|
|
74
74
|
`App.GMAIL` is the string `'gmail'`, so a plain string works anywhere a constant
|
|
75
75
|
does, including for an app added after your SDK version.
|
|
76
76
|
|
|
77
|
+
## 0.80.0: an agent can remember each end user
|
|
78
|
+
|
|
79
|
+
Nothing changes until you turn it on, and nothing in 0.79 breaks.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await agent.update({ memorySettings: { on: true } });
|
|
83
|
+
const conversation = await agent.start({ endUserId: 'u_ananya' });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
After a chat goes quiet, a background agent keeps a short memory about that end
|
|
87
|
+
user, on that agent only, and later chats use it. Your team can read and change
|
|
88
|
+
it. It needs a Team plan or higher.
|
|
89
|
+
|
|
90
|
+
| New in 0.80 | What it does |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `memorySettings` on `agent.update` and `ws.agents.create` | Turn it on, add your own instructions, set how long it is kept |
|
|
93
|
+
| `(await agent.get()).memorySettings.notice` | The line to show near your message box, if you draw your own chat |
|
|
94
|
+
| `agent.endUser(id).memory` | `get`, `set`, `delete` and `stop` |
|
|
95
|
+
| `agent.memories()` | Who has a memory on this agent, newest change first |
|
|
96
|
+
| `agent.start({ endUserId, memory: false })` | One conversation that uses nothing remembered and adds nothing |
|
|
97
|
+
|
|
98
|
+
The id you pass decides whose memory it is, so use one stable id per person. The
|
|
99
|
+
guide, with when you may need consent: https://docs.agentnava.com/memory
|
|
100
|
+
|
|
101
|
+
## 0.79.0 is a breaking change: an end user belongs to one agent
|
|
102
|
+
|
|
103
|
+
Each agent is its own app, with its own end users. The same id on two agents is
|
|
104
|
+
two different people, with separate accounts, secrets and conversations. A person
|
|
105
|
+
who uses two of your agents signs in to each.
|
|
106
|
+
|
|
107
|
+
Until now a person's connected account was found by workspace and id alone, so
|
|
108
|
+
agent B could use an account that a different person with the same id connected
|
|
109
|
+
through agent A. That is what this closes.
|
|
110
|
+
|
|
111
|
+
| Up to 0.78 | From 0.79 |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `ws.endUser(id)` | `agent.endUser(id)`. `ws.endUser` is gone |
|
|
114
|
+
| `ws.endUser(id).accounts.connect({ app })` | `agent.endUser(id).accounts.connect({ app })`, once per person on each agent |
|
|
115
|
+
| `ws.endUser(id).secrets.create({ name, value, agentId? })` | `agent.endUser(id).secrets.create({ name, value })` |
|
|
116
|
+
| `ws.secrets.create({ name, value, endUserId })` | `agent.endUser(endUserId).secrets.create({ name, value })` |
|
|
117
|
+
| `ws.endUser(id).conversations()`, across every agent | `agent.endUser(id).conversations()`, this agent. Ask each agent |
|
|
118
|
+
| `accounts.disconnectAll()` to off-board someone | `agent.endUser(id).erase()`: their accounts, secrets and conversations on this agent, and it returns how many of each |
|
|
119
|
+
|
|
120
|
+
**What an older SDK gets.** `ws.endUser(id).accounts.*` and `ws.endUser(id).secrets.*`
|
|
121
|
+
are refused with a 410 (`end_users_are_per_agent`) that says what to call instead.
|
|
122
|
+
An empty list would read as "nothing connected", so they never answer one.
|
|
123
|
+
`agent.start`, `conversation.ask`, and `conversation.accountsNeeded()` with its
|
|
124
|
+
sign-in link keep working on an older SDK: a conversation knows its agent.
|
|
125
|
+
|
|
126
|
+
**Existing accounts** were filed under the agent that used them, with no new
|
|
127
|
+
sign-in. One that several agents used went to none of them, because the same id may
|
|
128
|
+
be two people, so each agent asks the person to sign in. **Workspace members** are
|
|
129
|
+
unchanged: a member's own account belongs to no agent.
|
|
130
|
+
|
|
77
131
|
## 0.44.0 is a breaking change: apps and accounts
|
|
78
132
|
|
|
79
133
|
Two words now mean two things. An **account** is one saved sign-in to one app.
|
|
@@ -290,7 +344,7 @@ that one:
|
|
|
290
344
|
| Built with | Whose | Who signs in |
|
|
291
345
|
|---|---|---|
|
|
292
346
|
| `Connection.creatorAccount(app, account)` | the agent creator's | you or a colleague, once; every conversation uses it |
|
|
293
|
-
| `Connection.endUserAccount(app)` | the end user's | each person, once, remembered for them |
|
|
347
|
+
| `Connection.endUserAccount(app)` | the end user's | each person, once on each agent, remembered for them |
|
|
294
348
|
|
|
295
349
|
There is no fallback. An account that is not signed in leaves its connection
|
|
296
350
|
unavailable, and the agent says so rather than reaching for another account of
|
package/account.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ export declare class Account {
|
|
|
78
78
|
/**
|
|
79
79
|
* Wait until the person has finished signing in.
|
|
80
80
|
*
|
|
81
|
-
* The companion to `ws.accounts.connect()`, `
|
|
81
|
+
* The companion to `ws.accounts.connect()`, `agent.endUser(id).accounts
|
|
82
82
|
* .connect()` and `reconnect()`. Those hand you a url; this tells you when
|
|
83
83
|
* the person is done, so you do not write a polling loop or guess which
|
|
84
84
|
* field to believe.
|
package/account.js
CHANGED
|
@@ -103,7 +103,7 @@ export class Account {
|
|
|
103
103
|
/**
|
|
104
104
|
* Wait until the person has finished signing in.
|
|
105
105
|
*
|
|
106
|
-
* The companion to `ws.accounts.connect()`, `
|
|
106
|
+
* The companion to `ws.accounts.connect()`, `agent.endUser(id).accounts
|
|
107
107
|
* .connect()` and `reconnect()`. Those hand you a url; this tells you when
|
|
108
108
|
* the person is done, so you do not write a polling loop or guess which
|
|
109
109
|
* field to believe.
|
package/account.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account.js","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"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","sourcesContent":["// An account: one saved sign-in to one app.\n\nimport { AgentNavaError } from './errors.ts';\nimport type { Http } from './http.ts';\nimport type { AccountData, AccountStatus, UsedBy } from './types.ts';\n\n/**\n * One saved sign-in to one app, that you hold and act on.\n *\n * AN ACCOUNT IS NOT A CONNECTION, and the two words used to be one, which is\n * how an agent came to own a Gmail login that nothing else could see. An\n * ACCOUNT is a sign-in: Gmail, signed in as support@example.com. A CONNECTION is\n * an entry in an agent's setup that names one: \"this agent uses Gmail, through\n * that account\". Many agents can name one account, and reconnecting it once\n * fixes all of them.\n *\n * ```ts\n * const { account, url } = await ws.accounts.connect({ app: App.GMAIL, nickname: 'Support inbox' });\n * showToUser(url); // they sign in on Google's own page\n * await account.waitUntilConnected();\n * await agent.connections.add(Connection.creatorAccount(App.GMAIL, account));\n * ```\n *\n * Nobody handles a token, you included: the person signs in on the app's own\n * page, and this object says who they signed in as, never how.\n *\n * ONE KIND OF OBJECT FOR BOTH OWNERS. The agent creator's account and one of\n * your end users' are the same class, reached by the same routes: every method\n * here works on any account in the workspace, and `data.owner` says whose it\n * is. So `ws.account(id).waitUntilConnected()` is how you wait for an end user\n * to finish signing in, too.\n *\n * A CLASS, like `Database` and `Secret`: you hold it and act on it, rather than\n * threading an id through calls. Holding one makes no request. `ws.account(id)`\n * is free and the first call on it is the first round trip.\n */\nexport class Account {\n\treadonly id: string;\n\treadonly #http: Http;\n\tprivate snapshot?: AccountData;\n\n\tconstructor(http: Http, id: string, snapshot?: AccountData) {\n\t\tthis.#http = http;\n\t\tthis.id = id;\n\t\tif (snapshot) this.snapshot = snapshot;\n\t}\n\n\tprivate get base(): string {\n\t\treturn `/v1/accounts/${encodeURIComponent(this.id)}`;\n\t}\n\n\t/** The app's catalog id: `'gmail'`, `'hubspot'`. Named for what the person\n\t * signing in recognises, never for whoever we buy the sign-in from. */\n\tget app(): string | undefined {\n\t\treturn this.snapshot?.app;\n\t}\n\n\t/** Who it is signed in as: the email or username the app reports. Null until\n\t * somebody has finished signing in. */\n\tget label(): string | null | undefined {\n\t\treturn this.snapshot?.label;\n\t}\n\n\t/** Your own name for it, such as `'Support inbox'`. */\n\tget nickname(): string | null | undefined {\n\t\treturn this.snapshot?.nickname;\n\t}\n\n\t/** Where the sign-in stands, from the last read: `'pending'`, `'connected'`,\n\t * `'needs_sign_in'` or `'error'`.\n\t *\n\t * Reads the snapshot rather than the network. Undefined means this handle\n\t * has not read yet, NOT that the account is disconnected: call `get()`. */\n\tget status(): AccountStatus | undefined {\n\t\treturn this.snapshot?.status;\n\t}\n\n\t/** The agents whose setup names this account, from the last read. The\n\t * question to ask before disconnecting it: who loses it? */\n\tget usedBy(): UsedBy[] | undefined {\n\t\treturn this.snapshot?.usedBy;\n\t}\n\n\t/** The last-known record, from whatever call produced this object. */\n\tget data(): AccountData | undefined {\n\t\treturn this.snapshot;\n\t}\n\n\t/**\n\t * An account goes over the wire as its id, the way a `Date` goes as its ISO\n\t * string: `JSON.stringify({ account })` sends `\"acc_...\"`.\n\t *\n\t * That is what lets a connection written as a plain object,\n\t * `{ app, account }`, take the Account you are holding in\n\t * `ws.agents.create()` and `agent.update()`. It also matches the rule for\n\t * every handle in this SDK: store the id, not the object.\n\t */\n\ttoJSON(): string {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Read it back.\n\t *\n\t * A MISSING ACCOUNT THROWS (`account_not_found`, 404), and never comes back\n\t * as an empty record. An account that does not exist and one that is not\n\t * signed in yet are different answers. The connection this replaced once\n\t * answered a miss with a made-up blank, so storing an id and reading it back\n\t * gave you something that did not know what it was.\n\t */\n\tasync get(): Promise<AccountData> {\n\t\tconst res = await this.#http.get<{ account?: AccountData }>(this.base);\n\t\tthis.snapshot = accountIn(res, this.base);\n\t\treturn this.snapshot;\n\t}\n\n\t/**\n\t * Wait until the person has finished signing in.\n\t *\n\t * The companion to `ws.accounts.connect()`, `ws.endUser(id).accounts\n\t * .connect()` and `reconnect()`. Those hand you a url; this tells you when\n\t * the person is done, so you do not write a polling loop or guess which\n\t * field to believe.\n\t *\n\t * DONE MEANS CONNECTED AND NOT RECONNECTING. During a reconnect the account\n\t * keeps working (`status` stays `'connected'`) while `reconnecting` is true,\n\t * so a check on `status` alone would return before the person had signed in\n\t * again. This waits for both.\n\t *\n\t * THROWS on the deadline (`account_not_connected`), naming the account and\n\t * where it stands, rather than returning an account that is still pending.\n\t * Safe to call again: somebody who has not finished may just need longer.\n\t *\n\t * Polls rather than subscribing: there is a person in the middle, so a few\n\t * seconds costs nothing, and it works from any runtime without a socket.\n\t */\n\tasync waitUntilConnected(options: { timeoutMs?: number; intervalMs?: number } = {}): Promise<Account> {\n\t\tconst timeoutMs = options.timeoutMs ?? 120_000;\n\t\tconst intervalMs = options.intervalMs ?? 2_000;\n\t\tconst deadline = Date.now() + timeoutMs;\n\t\t// Checked before the first sleep: an account that is already connected\n\t\t// should cost one request, not one request plus an interval.\n\t\tlet state = await this.get();\n\t\twhile (state.status !== 'connected' || state.reconnecting) {\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new AgentNavaError({\n\t\t\t\t\tmessage: `Account ${this.id} (${state.app}) was not connected within ${timeoutMs}ms; it is still ${state.status}${\n\t\t\t\t\t\tstate.reconnecting ? ', with a reconnect waiting for the person' : ''\n\t\t\t\t\t}`,\n\t\t\t\t\tcode: 'account_not_connected',\n\t\t\t\t\tstatus: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait new Promise((r) => setTimeout(r, Math.min(intervalMs, Math.max(0, deadline - Date.now()))));\n\t\t\tstate = await this.get();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Send the person to sign in again, and get the link.\n\t *\n\t * What to call when an account `needs_sign_in`. Every agent that names it\n\t * follows, so reconnecting once fixes all of them. The sign-in that worked is\n\t * kept until the new one succeeds, so an abandoned link breaks nothing:\n\t * meanwhile `data.reconnecting` is true, and `waitUntilConnected()` waits\n\t * for it to clear.\n\t *\n\t * If the person signs in as SOMEBODY ELSE, that becomes a NEW account and\n\t * this one is left as it was, so no agent switches mailbox without anyone\n\t * deciding it should. List accounts to find the new one, then change the\n\t * connections that should use it.\n\t */\n\tasync reconnect(): Promise<{ url: string }> {\n\t\tconst res = await this.#http.post<{ url: string }>(`${this.base}/reconnect`);\n\t\treturn { url: res.url };\n\t}\n\n\t/** Give it your own name, such as `'Support inbox'`, shown beside who it is\n\t * signed in as. The app and the sign-in do not change. */\n\tasync rename(nickname: string): Promise<Account> {\n\t\tconst res = await this.#http.patch<{ account?: AccountData }>(this.base, { nickname });\n\t\tthis.snapshot = accountIn(res, this.base);\n\t\treturn this;\n\t}\n\n\t/** Ask the app whether the sign-in still works, and read the answer. A\n\t * revoked sign-in comes back as `needs_sign_in`, not as an exception: it is\n\t * the answer this call exists to get. */\n\tasync check(): Promise<AccountData> {\n\t\tconst res = await this.#http.post<{ account?: AccountData }>(`${this.base}/check`);\n\t\tthis.snapshot = accountIn(res, `${this.base}/check`);\n\t\treturn this.snapshot;\n\t}\n\n\t/**\n\t * Sign it out at the app and remove it here.\n\t *\n\t * ALWAYS ALLOWED, unlike deleting a database that an agent still names. It\n\t * returns the agents whose setup names the account, so you can tell whoever\n\t * owns them: from their next turn those connections report that the account\n\t * was disconnected. Nothing switches to another account in its place.\n\t *\n\t * Removing a connection from an agent is the other thing, and it never\n\t * touches the account: `agent.connections.remove(name)`.\n\t */\n\tasync disconnect(): Promise<{ agents: UsedBy[] }> {\n\t\t// The same route for either owner. An end user's account is never named\n\t\t// by an agent's setup (that says `endUser: true`), so for one of those\n\t\t// the list of agents is empty, which is the true answer.\n\t\tconst res = await this.#http.delete<{ disconnected?: boolean; agents?: UsedBy[] }>(this.base);\n\t\treturn { agents: res?.agents ?? [] };\n\t}\n}\n\n/**\n * The account a response carries, or a thrown error. Never a blank.\n *\n * A 404 already throws in the transport, with the API's `account_not_found`.\n * This covers the other way to end up with nothing: a 200 whose body has no\n * account in it, which would otherwise become a snapshot of `undefined` that\n * reads, at every call site, exactly like an account nobody has read yet.\n */\nexport function accountIn(res: { account?: AccountData } | undefined, path: string): AccountData {\n\tconst account = res?.account;\n\tif (!account || typeof account.id !== 'string') {\n\t\tthrow new AgentNavaError({\n\t\t\tmessage: `Expected an account from ${path}, got: ${JSON.stringify(res ?? null).slice(0, 200)}`,\n\t\t\tcode: 'bad_response',\n\t\t\tstatus: 200,\n\t\t});\n\t}\n\treturn account;\n}\n\n/** The id a connection sends for its account: an `Account` by its id, an id\n * as it is. */\nexport function accountIdOf(account: Account | string): string {\n\treturn typeof account === 'string' ? account : account?.id;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"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","sourcesContent":["// An account: one saved sign-in to one app.\n\nimport { AgentNavaError } from './errors.ts';\nimport type { Http } from './http.ts';\nimport type { AccountData, AccountStatus, UsedBy } from './types.ts';\n\n/**\n * One saved sign-in to one app, that you hold and act on.\n *\n * AN ACCOUNT IS NOT A CONNECTION, and the two words used to be one, which is\n * how an agent came to own a Gmail login that nothing else could see. An\n * ACCOUNT is a sign-in: Gmail, signed in as support@example.com. A CONNECTION is\n * an entry in an agent's setup that names one: \"this agent uses Gmail, through\n * that account\". Many agents can name one account, and reconnecting it once\n * fixes all of them.\n *\n * ```ts\n * const { account, url } = await ws.accounts.connect({ app: App.GMAIL, nickname: 'Support inbox' });\n * showToUser(url); // they sign in on Google's own page\n * await account.waitUntilConnected();\n * await agent.connections.add(Connection.creatorAccount(App.GMAIL, account));\n * ```\n *\n * Nobody handles a token, you included: the person signs in on the app's own\n * page, and this object says who they signed in as, never how.\n *\n * ONE KIND OF OBJECT FOR BOTH OWNERS. The agent creator's account and one of\n * your end users' are the same class, reached by the same routes: every method\n * here works on any account in the workspace, and `data.owner` says whose it\n * is. So `ws.account(id).waitUntilConnected()` is how you wait for an end user\n * to finish signing in, too.\n *\n * A CLASS, like `Database` and `Secret`: you hold it and act on it, rather than\n * threading an id through calls. Holding one makes no request. `ws.account(id)`\n * is free and the first call on it is the first round trip.\n */\nexport class Account {\n\treadonly id: string;\n\treadonly #http: Http;\n\tprivate snapshot?: AccountData;\n\n\tconstructor(http: Http, id: string, snapshot?: AccountData) {\n\t\tthis.#http = http;\n\t\tthis.id = id;\n\t\tif (snapshot) this.snapshot = snapshot;\n\t}\n\n\tprivate get base(): string {\n\t\treturn `/v1/accounts/${encodeURIComponent(this.id)}`;\n\t}\n\n\t/** The app's catalog id: `'gmail'`, `'hubspot'`. Named for what the person\n\t * signing in recognises, never for whoever we buy the sign-in from. */\n\tget app(): string | undefined {\n\t\treturn this.snapshot?.app;\n\t}\n\n\t/** Who it is signed in as: the email or username the app reports. Null until\n\t * somebody has finished signing in. */\n\tget label(): string | null | undefined {\n\t\treturn this.snapshot?.label;\n\t}\n\n\t/** Your own name for it, such as `'Support inbox'`. */\n\tget nickname(): string | null | undefined {\n\t\treturn this.snapshot?.nickname;\n\t}\n\n\t/** Where the sign-in stands, from the last read: `'pending'`, `'connected'`,\n\t * `'needs_sign_in'` or `'error'`.\n\t *\n\t * Reads the snapshot rather than the network. Undefined means this handle\n\t * has not read yet, NOT that the account is disconnected: call `get()`. */\n\tget status(): AccountStatus | undefined {\n\t\treturn this.snapshot?.status;\n\t}\n\n\t/** The agents whose setup names this account, from the last read. The\n\t * question to ask before disconnecting it: who loses it? */\n\tget usedBy(): UsedBy[] | undefined {\n\t\treturn this.snapshot?.usedBy;\n\t}\n\n\t/** The last-known record, from whatever call produced this object. */\n\tget data(): AccountData | undefined {\n\t\treturn this.snapshot;\n\t}\n\n\t/**\n\t * An account goes over the wire as its id, the way a `Date` goes as its ISO\n\t * string: `JSON.stringify({ account })` sends `\"acc_...\"`.\n\t *\n\t * That is what lets a connection written as a plain object,\n\t * `{ app, account }`, take the Account you are holding in\n\t * `ws.agents.create()` and `agent.update()`. It also matches the rule for\n\t * every handle in this SDK: store the id, not the object.\n\t */\n\ttoJSON(): string {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Read it back.\n\t *\n\t * A MISSING ACCOUNT THROWS (`account_not_found`, 404), and never comes back\n\t * as an empty record. An account that does not exist and one that is not\n\t * signed in yet are different answers. The connection this replaced once\n\t * answered a miss with a made-up blank, so storing an id and reading it back\n\t * gave you something that did not know what it was.\n\t */\n\tasync get(): Promise<AccountData> {\n\t\tconst res = await this.#http.get<{ account?: AccountData }>(this.base);\n\t\tthis.snapshot = accountIn(res, this.base);\n\t\treturn this.snapshot;\n\t}\n\n\t/**\n\t * Wait until the person has finished signing in.\n\t *\n\t * The companion to `ws.accounts.connect()`, `agent.endUser(id).accounts\n\t * .connect()` and `reconnect()`. Those hand you a url; this tells you when\n\t * the person is done, so you do not write a polling loop or guess which\n\t * field to believe.\n\t *\n\t * DONE MEANS CONNECTED AND NOT RECONNECTING. During a reconnect the account\n\t * keeps working (`status` stays `'connected'`) while `reconnecting` is true,\n\t * so a check on `status` alone would return before the person had signed in\n\t * again. This waits for both.\n\t *\n\t * THROWS on the deadline (`account_not_connected`), naming the account and\n\t * where it stands, rather than returning an account that is still pending.\n\t * Safe to call again: somebody who has not finished may just need longer.\n\t *\n\t * Polls rather than subscribing: there is a person in the middle, so a few\n\t * seconds costs nothing, and it works from any runtime without a socket.\n\t */\n\tasync waitUntilConnected(options: { timeoutMs?: number; intervalMs?: number } = {}): Promise<Account> {\n\t\tconst timeoutMs = options.timeoutMs ?? 120_000;\n\t\tconst intervalMs = options.intervalMs ?? 2_000;\n\t\tconst deadline = Date.now() + timeoutMs;\n\t\t// Checked before the first sleep: an account that is already connected\n\t\t// should cost one request, not one request plus an interval.\n\t\tlet state = await this.get();\n\t\twhile (state.status !== 'connected' || state.reconnecting) {\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new AgentNavaError({\n\t\t\t\t\tmessage: `Account ${this.id} (${state.app}) was not connected within ${timeoutMs}ms; it is still ${state.status}${\n\t\t\t\t\t\tstate.reconnecting ? ', with a reconnect waiting for the person' : ''\n\t\t\t\t\t}`,\n\t\t\t\t\tcode: 'account_not_connected',\n\t\t\t\t\tstatus: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait new Promise((r) => setTimeout(r, Math.min(intervalMs, Math.max(0, deadline - Date.now()))));\n\t\t\tstate = await this.get();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Send the person to sign in again, and get the link.\n\t *\n\t * What to call when an account `needs_sign_in`. Every agent that names it\n\t * follows, so reconnecting once fixes all of them. The sign-in that worked is\n\t * kept until the new one succeeds, so an abandoned link breaks nothing:\n\t * meanwhile `data.reconnecting` is true, and `waitUntilConnected()` waits\n\t * for it to clear.\n\t *\n\t * If the person signs in as SOMEBODY ELSE, that becomes a NEW account and\n\t * this one is left as it was, so no agent switches mailbox without anyone\n\t * deciding it should. List accounts to find the new one, then change the\n\t * connections that should use it.\n\t */\n\tasync reconnect(): Promise<{ url: string }> {\n\t\tconst res = await this.#http.post<{ url: string }>(`${this.base}/reconnect`);\n\t\treturn { url: res.url };\n\t}\n\n\t/** Give it your own name, such as `'Support inbox'`, shown beside who it is\n\t * signed in as. The app and the sign-in do not change. */\n\tasync rename(nickname: string): Promise<Account> {\n\t\tconst res = await this.#http.patch<{ account?: AccountData }>(this.base, { nickname });\n\t\tthis.snapshot = accountIn(res, this.base);\n\t\treturn this;\n\t}\n\n\t/** Ask the app whether the sign-in still works, and read the answer. A\n\t * revoked sign-in comes back as `needs_sign_in`, not as an exception: it is\n\t * the answer this call exists to get. */\n\tasync check(): Promise<AccountData> {\n\t\tconst res = await this.#http.post<{ account?: AccountData }>(`${this.base}/check`);\n\t\tthis.snapshot = accountIn(res, `${this.base}/check`);\n\t\treturn this.snapshot;\n\t}\n\n\t/**\n\t * Sign it out at the app and remove it here.\n\t *\n\t * ALWAYS ALLOWED, unlike deleting a database that an agent still names. It\n\t * returns the agents whose setup names the account, so you can tell whoever\n\t * owns them: from their next turn those connections report that the account\n\t * was disconnected. Nothing switches to another account in its place.\n\t *\n\t * Removing a connection from an agent is the other thing, and it never\n\t * touches the account: `agent.connections.remove(name)`.\n\t */\n\tasync disconnect(): Promise<{ agents: UsedBy[] }> {\n\t\t// The same route for either owner. An end user's account is never named\n\t\t// by an agent's setup (that says `endUser: true`), so for one of those\n\t\t// the list of agents is empty, which is the true answer.\n\t\tconst res = await this.#http.delete<{ disconnected?: boolean; agents?: UsedBy[] }>(this.base);\n\t\treturn { agents: res?.agents ?? [] };\n\t}\n}\n\n/**\n * The account a response carries, or a thrown error. Never a blank.\n *\n * A 404 already throws in the transport, with the API's `account_not_found`.\n * This covers the other way to end up with nothing: a 200 whose body has no\n * account in it, which would otherwise become a snapshot of `undefined` that\n * reads, at every call site, exactly like an account nobody has read yet.\n */\nexport function accountIn(res: { account?: AccountData } | undefined, path: string): AccountData {\n\tconst account = res?.account;\n\tif (!account || typeof account.id !== 'string') {\n\t\tthrow new AgentNavaError({\n\t\t\tmessage: `Expected an account from ${path}, got: ${JSON.stringify(res ?? null).slice(0, 200)}`,\n\t\t\tcode: 'bad_response',\n\t\t\tstatus: 200,\n\t\t});\n\t}\n\treturn account;\n}\n\n/** The id a connection sends for its account: an `Account` by its id, an id\n * as it is. */\nexport function accountIdOf(account: Account | string): string {\n\treturn typeof account === 'string' ? account : account?.id;\n}\n"]}
|
package/agent.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { type Account } from './account.ts';
|
|
2
2
|
import { Conversation } from './conversation.ts';
|
|
3
|
+
import { EndUser } from './end-user.ts';
|
|
3
4
|
import type { Http } from './http.ts';
|
|
4
5
|
import type { Secret } from './secret.ts';
|
|
5
6
|
import { type Turn } from './turn.ts';
|
|
6
7
|
import { type Connection } from './connection.ts';
|
|
7
|
-
import type { AgentData, AgentDraft, AgentFields, Bindings, ConnectionStatus, FileEntry, Trigger, WebsiteChat, WebsiteChatAccess, Workflow, WebhookDelivery } from './types.ts';
|
|
8
|
+
import type { AgentData, AgentDraft, AgentFields, Bindings, ConnectionStatus, FileEntry, Trigger, WebsiteChat, WebsiteChatAccess, Workflow, WebhookDelivery, MemoryData } from './types.ts';
|
|
8
9
|
export interface StartOptions {
|
|
9
10
|
/**
|
|
10
11
|
* Stored secrets to attach AS THE CONVERSATION STARTS.
|
|
11
12
|
*
|
|
12
|
-
* A `Secret` from `
|
|
13
|
+
* A `Secret` from `agent.endUser(id).secrets.create(...)` attaches by id, which
|
|
13
14
|
* is the only way a stored per-person credential can reach a turn: you never
|
|
14
15
|
* hold its value.
|
|
15
16
|
*
|
|
16
17
|
* ```ts
|
|
17
|
-
* const key = await
|
|
18
|
+
* const key = await agent.endUser(row.userId).secrets.create({ name: 'ZENDESK_TOKEN', value });
|
|
18
19
|
* const conversation = await agent.start({ endUserId: row.userId, attach: key });
|
|
19
20
|
* ```
|
|
20
21
|
*
|
|
@@ -35,6 +36,13 @@ export interface StartOptions {
|
|
|
35
36
|
* `conversation.accountsNeeded()` for the ones they have not connected yet.
|
|
36
37
|
*/
|
|
37
38
|
attach?: Secret | Secret[];
|
|
39
|
+
/**
|
|
40
|
+
* `false` starts this conversation with end-user memory off, when the agent has
|
|
41
|
+
* memory on: it uses nothing remembered and adds nothing. Fixed for the life of the
|
|
42
|
+
* conversation. On by default. The team can still read the conversation, so this
|
|
43
|
+
* is not a private chat.
|
|
44
|
+
*/
|
|
45
|
+
memory?: boolean;
|
|
38
46
|
/**
|
|
39
47
|
* Secret VALUES for this conversation only.
|
|
40
48
|
*
|
|
@@ -466,6 +474,43 @@ export declare class Agent {
|
|
|
466
474
|
conversation(conversationId: string, options?: {
|
|
467
475
|
widgets?: boolean;
|
|
468
476
|
}): Conversation;
|
|
477
|
+
/**
|
|
478
|
+
* One of YOUR end users of this agent. No request.
|
|
479
|
+
*
|
|
480
|
+
* The object everything about a person hangs off: their accounts, their
|
|
481
|
+
* secrets, their conversations, and `erase()`. You say who once, here, instead
|
|
482
|
+
* of threading an `endUserId` through each call and forgetting it in one of them.
|
|
483
|
+
*
|
|
484
|
+
* ```ts
|
|
485
|
+
* const ananya = agent.endUser(row.userId);
|
|
486
|
+
* const { url } = await ananya.accounts.connect({ app: App.GMAIL });
|
|
487
|
+
* await ananya.conversations();
|
|
488
|
+
* await ananya.erase();
|
|
489
|
+
* ```
|
|
490
|
+
*
|
|
491
|
+
* EACH AGENT IS ITS OWN APP, WITH ITS OWN END USERS. The same id on another agent
|
|
492
|
+
* is a different person: their own accounts, secrets and conversations. A person
|
|
493
|
+
* using two of your agents signs in to each.
|
|
494
|
+
*
|
|
495
|
+
* A SCOPE, NOT A RECORD. `ws.agent(id)`, `ws.project(id)`, `ws.account(id)` and
|
|
496
|
+
* `ws.secret(id)` point at rows we store and can list back. This points at
|
|
497
|
+
* nothing. We do not create your end users and we cannot list them. We only write
|
|
498
|
+
* the id you give us onto rows we already own.
|
|
499
|
+
*
|
|
500
|
+
* NOT a workspace member. `ws.members()` lists the colleagues who share this
|
|
501
|
+
* account, with ids we issued and authenticate. This takes an id from YOUR
|
|
502
|
+
* database that we store verbatim and never check.
|
|
503
|
+
*/
|
|
504
|
+
endUser(endUserId: string): EndUser;
|
|
505
|
+
/**
|
|
506
|
+
* Who has a memory on this agent, newest change first, at most 200 a call. For
|
|
507
|
+
* the next page, pass `before`: the `updatedAt` of the last one you got. Someone
|
|
508
|
+
* who asked to stop being remembered is listed, with `stopped: true`.
|
|
509
|
+
*/
|
|
510
|
+
memories(options?: {
|
|
511
|
+
limit?: number;
|
|
512
|
+
before?: string;
|
|
513
|
+
}): Promise<MemoryData[]>;
|
|
469
514
|
/**
|
|
470
515
|
* This agent's most recent conversations, newest first.
|
|
471
516
|
*
|
|
@@ -476,8 +521,7 @@ export declare class Agent {
|
|
|
476
521
|
* Object twin beside it, clamp identically).
|
|
477
522
|
*
|
|
478
523
|
* For the next page, pass `before`: the `startedAt` of the last conversation
|
|
479
|
-
* you got. `
|
|
480
|
-
* one person's history rather than one agent's.
|
|
524
|
+
* you got. `agent.endUser(id).conversations()` is the same list for one person.
|
|
481
525
|
*
|
|
482
526
|
* `endUser` narrows to one of YOUR end users, which is what you want for a
|
|
483
527
|
* "your conversations" list. Leaving it out returns everyone's, so leave it
|
package/agent.js
CHANGED
|
@@ -3,6 +3,7 @@ var _a;
|
|
|
3
3
|
import { accountIdOf } from "./account.js";
|
|
4
4
|
import { isBindings, refuseConnections, secretIdsOf, secretsIn } from "./attach.js";
|
|
5
5
|
import { Conversation } from "./conversation.js";
|
|
6
|
+
import { EndUser } from "./end-user.js";
|
|
6
7
|
import { AgentNavaError } from "./errors.js";
|
|
7
8
|
import { makeTurn } from "./turn.js";
|
|
8
9
|
import { connectionForWire } from "./connection.js";
|
|
@@ -536,6 +537,7 @@ export class Agent {
|
|
|
536
537
|
agentId: this.id,
|
|
537
538
|
...(options.version !== undefined && { version: options.version }),
|
|
538
539
|
...(options.parameters && { parameters: options.parameters }),
|
|
540
|
+
...(options.memory !== undefined && { memory: options.memory }),
|
|
539
541
|
// `sessionBindings`, NOT `secrets` at the top level.
|
|
540
542
|
//
|
|
541
543
|
// This sent them at the top level and the launch route reads
|
|
@@ -585,6 +587,48 @@ export class Agent {
|
|
|
585
587
|
conversation(conversationId, options = {}) {
|
|
586
588
|
return new Conversation(this.#http, conversationId, undefined, { widgets: options.widgets === true });
|
|
587
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* One of YOUR end users of this agent. No request.
|
|
592
|
+
*
|
|
593
|
+
* The object everything about a person hangs off: their accounts, their
|
|
594
|
+
* secrets, their conversations, and `erase()`. You say who once, here, instead
|
|
595
|
+
* of threading an `endUserId` through each call and forgetting it in one of them.
|
|
596
|
+
*
|
|
597
|
+
* ```ts
|
|
598
|
+
* const ananya = agent.endUser(row.userId);
|
|
599
|
+
* const { url } = await ananya.accounts.connect({ app: App.GMAIL });
|
|
600
|
+
* await ananya.conversations();
|
|
601
|
+
* await ananya.erase();
|
|
602
|
+
* ```
|
|
603
|
+
*
|
|
604
|
+
* EACH AGENT IS ITS OWN APP, WITH ITS OWN END USERS. The same id on another agent
|
|
605
|
+
* is a different person: their own accounts, secrets and conversations. A person
|
|
606
|
+
* using two of your agents signs in to each.
|
|
607
|
+
*
|
|
608
|
+
* A SCOPE, NOT A RECORD. `ws.agent(id)`, `ws.project(id)`, `ws.account(id)` and
|
|
609
|
+
* `ws.secret(id)` point at rows we store and can list back. This points at
|
|
610
|
+
* nothing. We do not create your end users and we cannot list them. We only write
|
|
611
|
+
* the id you give us onto rows we already own.
|
|
612
|
+
*
|
|
613
|
+
* NOT a workspace member. `ws.members()` lists the colleagues who share this
|
|
614
|
+
* account, with ids we issued and authenticate. This takes an id from YOUR
|
|
615
|
+
* database that we store verbatim and never check.
|
|
616
|
+
*/
|
|
617
|
+
endUser(endUserId) {
|
|
618
|
+
return new EndUser(this.#http, this.id, endUserId);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Who has a memory on this agent, newest change first, at most 200 a call. For
|
|
622
|
+
* the next page, pass `before`: the `updatedAt` of the last one you got. Someone
|
|
623
|
+
* who asked to stop being remembered is listed, with `stopped: true`.
|
|
624
|
+
*/
|
|
625
|
+
async memories(options = {}) {
|
|
626
|
+
const res = await this.#http.get(`${this.base}/memories`, {
|
|
627
|
+
...(options.limit !== undefined && { limit: options.limit }),
|
|
628
|
+
...(options.before && { before: options.before }),
|
|
629
|
+
});
|
|
630
|
+
return res.memories ?? [];
|
|
631
|
+
}
|
|
588
632
|
/**
|
|
589
633
|
* This agent's most recent conversations, newest first.
|
|
590
634
|
*
|
|
@@ -595,8 +639,7 @@ export class Agent {
|
|
|
595
639
|
* Object twin beside it, clamp identically).
|
|
596
640
|
*
|
|
597
641
|
* For the next page, pass `before`: the `startedAt` of the last conversation
|
|
598
|
-
* you got. `
|
|
599
|
-
* one person's history rather than one agent's.
|
|
642
|
+
* you got. `agent.endUser(id).conversations()` is the same list for one person.
|
|
600
643
|
*
|
|
601
644
|
* `endUser` narrows to one of YOUR end users, which is what you want for a
|
|
602
645
|
* "your conversations" list. Leaving it out returns everyone's, so leave it
|
package/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["// An agent: a configuration you start conversations from.\n\nimport { type Account, accountIdOf } from './account.ts';\nimport { isBindings, refuseConnections, secretIdsOf, secretsIn } from './attach.ts';\nimport { Conversation } from './conversation.ts';\nimport { AgentNavaError } from './errors.ts';\nimport type { Http } from './http.ts';\nimport type { Secret } from './secret.ts';\nimport { makeTurn, type Turn } from './turn.ts';\nimport { type Connection, connectionForWire } from './connection.ts';\nimport type {\n\tAgentConnection,\n\tAgentConversationData,\n\tAgentData,\n\tAgentDraft,\n\tAgentFields,\n\tBindings,\n\tConnectionStatus,\n\tFileEntry,\n\tTrigger,\n\tWebsiteChat,\n\tWebsiteChatAccess,\n\tWorkflow,\n\tWebhookDelivery,\n} from './types.ts';\n\nexport interface StartOptions {\n\t/**\n\t * Stored secrets to attach AS THE CONVERSATION STARTS.\n\t *\n\t * A `Secret` from `ws.endUser(id).secrets.create(...)` attaches by id, which\n\t * is the only way a stored per-person credential can reach a turn: you never\n\t * hold its value.\n\t *\n\t * ```ts\n\t * const key = await ws.endUser(row.userId).secrets.create({ name: 'ZENDESK_TOKEN', value });\n\t * const conversation = await agent.start({ endUserId: row.userId, attach: key });\n\t * ```\n\t *\n\t * Doing it here rather than with a second `conversation.attach()` call\n\t * closes a real window: between `start` and the attach, the conversation\n\t * exists and can be asked something, and it would answer without it.\n\t *\n\t * The conversation keeps a POINTER to the secret, never a copy. Replacing\n\t * its value (`secret.update(value)`) reaches the conversation on its next\n\t * tool call, and deleting the secret stops the conversation using it at\n\t * once.\n\t *\n\t * ACCOUNTS ARE NOT ATTACHED HERE any more (0.44), and one handed in is\n\t * refused. The agent's setup names the account each connection uses; for a\n\t * connection to the end user's account (`Connection.endUserAccount`), the\n\t * turn uses the account of the person this conversation is for, found by\n\t * `endUserId`. Ask\n\t * `conversation.accountsNeeded()` for the ones they have not connected yet.\n\t */\n\tattach?: Secret | Secret[];\n\n\t/**\n\t * Secret VALUES for this conversation only.\n\t *\n\t * Not `attach`, because a secret has no id you can hand over: binding one\n\t * takes the value itself. That asymmetry is in the product, not in this\n\t * naming, and hiding it behind the same word would not remove it.\n\t *\n\t * A value given here EXPIRES, after `secretsExpireIn` seconds (one hour by\n\t * default). After that the agent's tools say the sign-in expired, the\n\t * conversation lists the name in `requires`, and a turn starts with a\n\t * `credential_needed` event. Push a fresh value with\n\t * `conversation.ask(text, { secrets })` or `conversation.attach({ secrets })`.\n\t */\n\tsecrets?: Record<string, string>;\n\n\t/** How long the values in `secrets` stay usable, in seconds. Default 3600\n\t * (one hour), at most 86400 (one day). */\n\tsecretsExpireIn?: number;\n\n\t/** Pin a specific version. Defaults to the latest at the moment of starting.\n\t *\n\t * A conversation pins its version for life: publishing afterwards does not\n\t * move a conversation that is already open. The one exception is a schedule\n\t * or webhook that names the conversation (`conversationId`): each firing\n\t * moves it to the latest version, so a fix you publish reaches it. */\n\tversion?: number;\n\tparameters?: Record<string, string | number>;\n\n\t/**\n\t * WHO this conversation is for: one of YOUR users, not one of ours.\n\t *\n\t * REQUIRED, and never null. Every conversation belongs to someone. The id\n\t * is yours to choose: a user's row id, an anonymous visitor's random id, or\n\t * a name for a job such as `'nightly-report'`. Only a non-empty string is\n\t * accepted; anything else throws before a request is sent. (Decided\n\t * 2026-09-26, AGE-161: a conversation with no owner cannot be listed back to\n\t * the person it was for, and the accounts connected inside it could not be\n\t * revoked with that person.)\n\t *\n\t * It LABELS the conversation, and it decides one thing more: a connection\n\t * to the end user's account (`Connection.endUserAccount`) uses THIS\n\t * person's account for that app.\n\t * It does not decide which conversation you get, because `start` always\n\t * starts a new one. Later you can ask for everything belonging to that\n\t * person:\n\t *\n\t * ```ts\n\t * const conversation = await agent.start({ endUserId: row.userId });\n\t * const nightly = await agent.start({ endUserId: 'nightly-report' }); // a job has an id too\n\t * const theirs = await agent.conversations({ endUser: row.userId });\n\t * ```\n\t *\n\t * To CONTINUE a conversation, keep its id and address it directly. That is\n\t * the only way to continue one, on purpose:\n\t *\n\t * ```ts\n\t * await ws.agent(AGENT).conversation(row.conversationId).ask('and the rest?');\n\t * ```\n\t *\n\t * The id is YOURS and opaque to us: a row id from your database, not an\n\t * email and not something we authenticate. We never verify it, so it is\n\t * exactly as trustworthy as whatever holds your API key.\n\t *\n\t * The platform's prefixes are refused with a 400 (`end_user_reserved`):\n\t * `member:`, `visitor:` and `job:`, which name a member in the web app, an\n\t * embed visitor and work nobody is typing, and the older `webhook:`,\n\t * `builder:` and `librarian:`. Case and spacing do not matter to the check\n\t * (`MEMBER:x` is refused too). An id you chose must never be mistaken for\n\t * one of ours.\n\t *\n\t * NOT A WORKSPACE MEMBER'S ID. `ws.members()` lists the colleagues who share\n\t * this account, and their `userId` is a platform id we issued. This field is\n\t * a different namespace entirely. Both were once spelled `userId`, so\n\t * passing a member id here was a call that could not fail: there is nothing\n\t * for an end-user id to be checked against, so it just labelled the\n\t * conversation with a colleague's internal id and looked fine.\n\t */\n\tendUserId: string;\n\t/**\n\t * This conversation's screen can draw widgets: cards, tables and charts the\n\t * agent writes inside its replies, in Google's open A2UI format. Off unless set.\n\t * When on, each turn's stream carries `widget` events and `done.widgets`,\n\t * and `messages()` returns each reply's `widgets`. The agent draws only if\n\t * its own \"Answer with widgets\" setting is on (it is, unless turned off).\n\t * Draw them with `ReplyView` from `@cerebro-labs/agentnava-sdk/react`, or your own components.\n\t */\n\twidgets?: boolean;\n}\n\n/**\n * An agent you can configure and start conversations from.\n *\n * Holding one makes NO request: `ws.agent(id)` is free and the first call on it\n * is the first round trip.\n *\n * EVERY METHOD THAT CHANGES THE AGENT PUBLISHES A VERSION. There is no separate\n * save. What you do not send is inherited from the current version, which is the\n * only reason changing one field is safe: a resend that rebuilt the whole\n * configuration would drop whatever the caller forgot, and that bug has shipped\n * here four separate times.\n */\nexport class Agent {\n\treadonly id: string;\n\treadonly #http: Http;\n\tprivate snapshot?: AgentData;\n\n\tconstructor(http: Http, id: string, snapshot?: AgentData) {\n\t\tthis.#http = http;\n\t\tthis.id = id;\n\t\tif (snapshot) this.snapshot = snapshot;\n\t}\n\n\tprivate get base(): string {\n\t\treturn `/v1/agents/${encodeURIComponent(this.id)}`;\n\t}\n\n\t/** The last-known configuration, from whatever call produced this object.\n\t * Undefined on an agent you addressed by id and have not read. */\n\tget data(): AgentData | undefined {\n\t\treturn this.snapshot;\n\t}\n\n\tasync get(options: { version?: number } = {}): Promise<AgentData> {\n\t\tconst data = await this.#http.get<AgentData>(\n\t\t\tthis.base,\n\t\t\toptions.version !== undefined ? { version: options.version } : undefined,\n\t\t);\n\t\t// Only cache the CURRENT configuration. Caching a historical version would\n\t\t// make `agent.data` mean different things depending on the last call.\n\t\tif (options.version === undefined) this.snapshot = data;\n\t\treturn data;\n\t}\n\n\t/** Every version, newest first. */\n\tasync versions(): Promise<AgentData[]> {\n\t\tconst res = await this.#http.get<{ versions: AgentData[] }>(`${this.base}/versions`);\n\t\treturn res.versions ?? [];\n\t}\n\n\t/** Publish a new version with these fields changed and everything else kept.\n\t * `connections`, when sent, replaces the whole list; an `Account` in it goes\n\t * over the wire as its id (`Account.toJSON`). */\n\tasync update(fields: AgentFields): Promise<Agent> {\n\t\tconst data = await this.#http.post<AgentData>(`${this.base}/versions`, fields);\n\t\tthis.snapshot = data;\n\t\treturn new Agent(this.#http, this.id, data);\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tawait this.#http.delete(this.base);\n\t}\n\n\t// ----------------------------------------------------- draft and builder\n\n\t/**\n\t * The Agent Builder, the one behind the portal's Configure chat, working on\n\t * this agent. Most of what it changes lands in the agent's DRAFT, read with\n\t * `draft()`; two of its tools act at once, renaming the agent and importing\n\t * a website into the agent's files. The draft is the one the portal edits,\n\t * shared with anyone changing the agent there, and each message joins the\n\t * agent's one Configure conversation. So to take only part of what it writes,\n\t * run it on an agent nobody else edits (a copy made for the purpose), read\n\t * that copy's draft, publish what you keep on the real agent with\n\t * `update()`, and delete the copy. Builder messages cost no credits and\n\t * count towards a daily limit per workspace.\n\t */\n\treadonly builder = {\n\t\t/** Send the Builder one message. Await the turn for its reply, or iterate it for the events. `signal` stops waiting for it. */\n\t\task: (message: string, options: { signal?: AbortSignal } = {}): Turn =>\n\t\t\tmakeTurn(this.#http.send(`${this.base}/build`, { method: 'POST', body: { message }, ...(options.signal && { signal: options.signal }) })),\n\t};\n\n\t/** The agent as its draft holds it, and whether that draft has changes not yet published. With no draft, the live version. */\n\tasync draft(): Promise<AgentDraft> {\n\t\tconst res = await this.#http.get<DraftWire>(`${this.base}/spec`);\n\t\treturn {\n\t\t\texists: res.isDraft === true,\n\t\t\thasUnpublishedChanges: res.hasUnpublishedChanges === true,\n\t\t\tconfig: res.spec,\n\t\t};\n\t}\n\n\t// --------------------------------------------------------------- workflows\n\n\t/**\n\t * Add a workflow, or replace the one with the same name.\n\t *\n\t * Reads the current list and sends it back with this one in place, because\n\t * the API replaces the whole list: a workflow set is a list, and there is no\n\t * \"merge one item\" that does not have to decide what a duplicate name means.\n\t * Deciding it here, once, is better than every caller deciding it.\n\t */\n\tasync setWorkflow(workflow: Workflow): Promise<Agent> {\n\t\tconst current = this.snapshot?.workflows ?? (await this.get()).workflows;\n\t\tconst next = current.filter((w) => w.name !== workflow.name).concat(workflow);\n\t\treturn this.update({ workflows: next });\n\t}\n\n\tasync removeWorkflow(name: string): Promise<Agent> {\n\t\tconst current = this.snapshot?.workflows ?? (await this.get()).workflows;\n\t\treturn this.update({ workflows: current.filter((w) => w.name !== name) });\n\t}\n\n\t/** Replace the trigger list. Sending `[]` removes every trigger, including\n\t * `chat`, which is what leaves an agent that only runs on a schedule. */\n\tasync setTriggers(triggers: Trigger[]): Promise<Agent> {\n\t\treturn this.update({ triggers });\n\t}\n\n\t/**\n\t * Replace the webhook's URL and return the new one.\n\t *\n\t * The old URL stops working the moment this returns, so update the system\n\t * that calls it. Use it when a URL leaked. The webhook's `message` and who it\n\t * runs for are kept. Publishes a version. Throws `no_webhook` (409) when the\n\t * agent has no webhook trigger.\n\t */\n\tasync rotateWebhookUrl(): Promise<string> {\n\t\tconst data = await this.#http.post<AgentData>(`${this.base}/webhook/rotate`, {});\n\t\tthis.snapshot = data;\n\t\tconst webhook = data.triggers.find((t) => t.kind === 'webhook');\n\t\tif (webhook?.kind !== 'webhook' || !webhook.url) {\n\t\t\tthrow new AgentNavaError({\n\t\t\t\tmessage: 'The webhook was rotated but no URL came back. Read it with agent.get().',\n\t\t\t\tcode: 'webhook_url_missing',\n\t\t\t\tstatus: 502,\n\t\t\t});\n\t\t}\n\t\treturn webhook.url;\n\t}\n\n\t/** What the webhook received, newest first: accepted, refused (and why), a\n\t * sender's retry recognised as a repeat, or a test. Up to `limit` (200). */\n\tasync webhookDeliveries(options: { limit?: number } = {}): Promise<WebhookDelivery[]> {\n\t\tconst res = await this.#http.get<{ deliveries: WebhookDelivery[] }>(\n\t\t\t`${this.base}/webhook/deliveries`,\n\t\t\toptions.limit !== undefined ? { limit: options.limit } : undefined,\n\t\t);\n\t\treturn res.deliveries ?? [];\n\t}\n\n\t/**\n\t * Send the webhook a sample event and run the agent on it, the way a real\n\t * delivery would (the signature check aside). A real turn, charged like\n\t * one. Returns the conversation it ran in.\n\t */\n\tasync sendTestWebhook(): Promise<{ conversationId?: string }> {\n\t\tconst res = await this.#http.post<{ conversationId?: string }>(`${this.base}/webhook/test`, {});\n\t\treturn res.conversationId ? { conversationId: res.conversationId } : {};\n\t}\n\n\t// ------------------------------------------------------------ website chat\n\n\t/**\n\t * The agent's website chat: its chat page on AgentNava (the share link),\n\t * and the embed script that puts it on your website. Every agent has one\n\t * from the moment it is created, however it was made (here, through the\n\t * API, or in the portal), except an agent that needs each user's own\n\t * account or secret, which a visitor cannot bring.\n\t *\n\t * Who can chat is one setting, `access`:\n\t *\n\t * 'restricted' the default. The share link works for members of your\n\t * workspace who are signed in to AgentNava, so your team can\n\t * pass it around to test. The chat shows on another website\n\t * only when that website is on `allowedWebsites`.\n\t * 'anyone' anyone with the link can chat, and any website can show\n\t * the chat. Each visitor's turns are charged to your\n\t * workspace.\n\t *\n\t * You never need it to use the agent from code: the agent answers your API\n\t * key, and the portal's Test, as soon as it exists. Publishing a new version\n\t * moves the website chat to it, as it moves new conversations.\n\t */\n\treadonly websiteChat = {\n\t\t/** Upload a logo for the chat's look: PNG, JPG, WebP or SVG, up to 1 MB\n\t\t * (an SVG carrying script is refused). Returns its id, to set as\n\t\t * `appearance.logo` with `update()`, and where it is served. The same\n\t\t * bytes always give the same id. */\n\t\tuploadLogo: async (bytes: Uint8Array | ArrayBuffer, contentType?: string): Promise<{ id: string; url: string }> =>\n\t\t\tthis.#http.request<{ id: string; url: string }>(`${this.base}/images`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tbytes,\n\t\t\t\t...(contentType && { contentType }),\n\t\t\t}),\n\t\t/** Who can chat, the share link, and the id the embed script's\n\t\t * `data-agent-id` takes. */\n\t\tget: async (): Promise<WebsiteChat> => toWebsiteChat(\n\t\t\tawait this.#http.get<WebsiteChatWire>(`${this.base}/website-chat-access`),\n\t\t),\n\t\t/** Who can chat: 'restricted' (your workspace's signed-in members, and\n\t\t * the allowed websites) or 'anyone' (anyone with the link). */\n\t\taccess: {\n\t\t\t/** The setting as it stands. */\n\t\t\tget: async (): Promise<WebsiteChatAccess> =>\n\t\t\t\t(await this.#http.get<WebsiteChatWire>(`${this.base}/website-chat-access`)).access,\n\t\t\t/** Change it. Returns the setting as stored. Anything but 'restricted'\n\t\t\t * or 'anyone' is refused (400). */\n\t\t\tset: async (access: WebsiteChatAccess): Promise<WebsiteChatAccess> =>\n\t\t\t\t(await this.#http.put<WebsiteChatWire>(`${this.base}/website-chat-access`, { access })).access,\n\t\t},\n\t\t/** The websites a Restricted agent's chat may be shown on through the\n\t\t * embed script, where visitors chat without signing in. A domain covers\n\t\t * its subdomains; an empty list means no other website. While access is\n\t\t * 'anyone' the list is kept and not needed. */\n\t\tallowedWebsites: {\n\t\t\t/** The list as it stands. */\n\t\t\tget: async (): Promise<string[]> =>\n\t\t\t\t(await this.#http.get<{ websites: string[] }>(`${this.base}/allowed-websites`)).websites,\n\t\t\t/** Replace the list. Each entry is normalised (\"https://acme.com/page\"\n\t\t\t * becomes \"acme.com\"; \"localhost\" is allowed for testing), and one that\n\t\t\t * is not a website is refused (400). Returns the list as stored. */\n\t\t\tset: async (websites: string[]): Promise<string[]> =>\n\t\t\t\t(await this.#http.put<{ websites: string[] }>(`${this.base}/allowed-websites`, { websites })).websites,\n\t\t},\n\t};\n\n\t// --------------------------------------------------------------- knowledge\n\n\t#readKnowledgeResponse(path: string): Promise<Response> {\n\t\treturn this.#http.send(`${this.base}/knowledge/files/content`, { query: { path } });\n\t}\n\n\t/**\n\t * The agent's knowledge: files every conversation can read.\n\t *\n\t * BY REFERENCE, not by copy. A conversation reads the knowledge as it is now,\n\t * so replacing a file changes what open conversations see on their next turn.\n\t * That is the opposite of how a version works, and it is deliberate: a price\n\t * list that could not be corrected without republishing the agent would be\n\t * useless.\n\t */\n\treadonly knowledge = {\n\t\tput: async (path: string, bytes: Uint8Array | ArrayBuffer, contentType?: string): Promise<FileEntry> =>\n\t\t\tthis.#http.request<FileEntry>(`${this.base}/knowledge/files`, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\tbytes,\n\t\t\t\tquery: { path },\n\t\t\t\t...(contentType && { contentType }),\n\t\t\t}),\n\t\t/** Read an agent-owned knowledge file as bytes, without starting a turn. */\n\t\tget: async (path: string): Promise<Uint8Array> => {\n\t\t\tconst res = await this.#readKnowledgeResponse(path);\n\t\t\treturn new Uint8Array(await res.arrayBuffer());\n\t\t},\n\t\t/** Read UTF-8 text. JSON files remain text rather than being parsed. */\n\t\ttext: async (path: string): Promise<string> => {\n\t\t\tconst res = await this.#readKnowledgeResponse(path);\n\t\t\treturn res.text();\n\t\t},\n\t\tlist: async (path = '/'): Promise<FileEntry[]> => {\n\t\t\t// The route returns EVERY knowledge file for the agent; it takes no\n\t\t\t// directory parameter. Filtering here rather than sending one the server\n\t\t\t// ignores, because a call that looks filtered and is not is worse than\n\t\t\t// no filter at all. Knowledge sets are small, so one read is right.\n\t\t\tconst res = await this.#http.get<{ files: (FileEntry & { path: string })[] }>(\n\t\t\t\t`${this.base}/knowledge/files`,\n\t\t\t);\n\t\t\tconst prefix = path === '/' ? '/' : `${path.replace(/\\/+$/, '')}/`;\n\t\t\treturn (res.files ?? []).filter((f) => f.path?.startsWith(prefix));\n\t\t},\n\t\tremove: async (path: string): Promise<void> => {\n\t\t\tawait this.#http.delete(`${this.base}/knowledge/files`, { path });\n\t\t},\n\t};\n\n\t// -------------------------------------------------------- knowledge bases\n\n\t/**\n\t * The WORKSPACE knowledge bases this agent reads.\n\t *\n\t * Different from `knowledge` above, and the distinction is the whole point.\n\t * `agent.knowledge` is files this agent owns; nobody else has them. A\n\t * knowledge base is shared: one price list, attached to nine agents, corrected\n\t * in one place.\n\t *\n\t * Each attached base is mounted read-only at `/knowledge/<key>/` when a turn\n\t * starts, so the agent reads it with `ls`, `grep` and `cat`.\n\t *\n\t * ATTACHING PUBLISHES A VERSION. Which bases an agent reads is part of its\n\t * configuration, so the change is diffable and reversible like any other\n\t * configuration change. The CONTENTS of a base are not versioned: correcting\n\t * a file changes what every attached agent sees on its next turn.\n\t */\n\treadonly knowledgeBases = {\n\t\t/**\n\t\t * Attach one base by key.\n\t\t *\n\t\t * One call, not read-modify-write, deliberately. Two attaches racing\n\t\t * through a read of the current list and a write of the whole list would\n\t\t * lose one of them silently, and losing an attachment looks exactly like\n\t\t * an agent that cannot find its knowledge. The server appends.\n\t\t *\n\t\t * Attaching a key that names no base is refused, naming the key. Attaching\n\t\t * one that is already attached is a no-op, not an error.\n\t\t */\n\t\tattach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.post<AgentData>(`${this.base}/knowledge-bases`, { key });\n\t\t\tthis.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** Detach one base. The base itself and its files are untouched: this only\n\t\t * stops THIS agent from reading it. Detaching one that is not attached is\n\t\t * a no-op. */\n\t\tdetach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.delete<AgentData>(\n\t\t\t\t`${this.base}/knowledge-bases/${encodeURIComponent(key)}`,\n\t\t\t);\n\t\t\tif (res) this.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** The keys this agent currently reads, in configuration order. */\n\t\tlist: async (): Promise<string[]> => {\n\t\t\tconst current = this.snapshot ?? (await this.get());\n\t\t\treturn current.knowledgeBases ?? [];\n\t\t},\n\t};\n\n\t// -------------------------------------------------------------- databases\n\n\t/**\n\t * The WORKSPACE databases this agent may query, by key.\n\t *\n\t * Register a database first with `ws.databases.create()`, which gives it its\n\t * key; attaching names that key in this agent's configuration. Naming it is\n\t * the grant: the database answers this agent's `sql_*` calls and no agent\n\t * that does not name it, and the agent's prompt lists it by key.\n\t *\n\t * ATTACHING PUBLISHES A VERSION, like a knowledge base: which databases an\n\t * agent reaches is part of its configuration, so the change is diffable and\n\t * reversible. Detaching takes effect on the agent's next query, including in\n\t * a conversation already under way.\n\t */\n\treadonly databases = {\n\t\t/** Attach one database by key. The server appends, so two attaches cannot\n\t\t * lose each other. A key that names no database is refused; one already\n\t\t * attached is a no-op. */\n\t\tattach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.post<AgentData>(`${this.base}/databases`, { key });\n\t\t\tthis.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** Stop this agent querying one database. The database stays registered\n\t\t * for every other agent that names it. */\n\t\tdetach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.delete<AgentData>(`${this.base}/databases/${encodeURIComponent(key)}`);\n\t\t\tif (res) this.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** The keys this agent may query, in configuration order. */\n\t\tlist: async (): Promise<string[]> => {\n\t\t\tconst current = this.snapshot ?? (await this.get());\n\t\t\treturn current.databases ?? [];\n\t\t},\n\t};\n\n\t// ------------------------------------------------------------ connections\n\n\t/**\n\t * The apps this agent uses: each a NAMED connection that says WHOSE ACCOUNT\n\t * it uses, built the way the portal's Add a connection screen asks.\n\t *\n\t * | built with | whose account | the person signs in |\n\t * |---|---|---|\n\t * | `Connection.creatorAccount(app, account)` | the agent creator's, from `ws.accounts.connect()` | once, and every conversation uses it |\n\t * | `Connection.endUserAccount(app)` | the end user's: each person talking to the agent | once each, remembered for them |\n\t *\n\t * ```ts\n\t * const { account: work } = await ws.accounts.connect({ app: App.GMAIL, nickname: 'Work' });\n\t * await agent.connections.add(Connection.creatorAccount(App.GMAIL, work, { name: 'work-gmail' }));\n\t * await agent.connections.add(Connection.endUserAccount(App.GOOGLE_CALENDAR));\n\t * ```\n\t *\n\t * A turn uses EXACTLY the account the setup names, and never another one.\n\t * There is no fallback to whichever account of that app happens to exist:\n\t * that fallback is what this replaced, and it meant reading an agent's code\n\t * told you nothing about whose mailbox it read.\n\t *\n\t * EVERY CHANGE PUBLISHES A VERSION, like `databases`: which accounts an\n\t * agent reaches is part of its configuration, so the change is diffable and\n\t * reversible. A conversation uses the connections of the version it runs.\n\t * The ACCOUNTS are not versioned: signing one in again reaches every agent\n\t * that names it, from the next turn.\n\t *\n\t * Removing a connection never touches the account. Disconnecting an account\n\t * is the other operation, on the account: `account.disconnect()`.\n\t */\n\treadonly connections = {\n\t\t/**\n\t\t * Add one connection. One call that publishes, not read-modify-write, so\n\t\t * two adds racing cannot lose each other.\n\t\t *\n\t\t * Takes what `Connection.creatorAccount()` or `Connection.endUserAccount()`\n\t\t * builds, or the same plain object. Refused, naming the problem, when the\n\t\t * name is taken on this agent (`connection_name_taken`), when the account\n\t\t * is of a different app (`account_app_mismatch`), or when no account has\n\t\t * that id (`account_not_found`).\n\t\t */\n\t\tadd: async (connection: Connection): Promise<Agent> => {\n\t\t\tconst res = await this.#http.post<AgentData>(`${this.base}/connections`, connectionForWire(connection));\n\t\t\tthis.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/**\n\t\t * Point one connection at another account: `{ account }` for the agent\n\t\t * creator's (the `Account` you hold, or its id), or `{ endUser: true }`\n\t\t * for each end user's own. The name and the app stay; an account of a\n\t\t * different app is refused (`account_app_mismatch`).\n\t\t *\n\t\t * `tools` changes which of the app's own tools the agent may use (a list\n\t\t * of ids, `[]` for none, `null` for the app's default), alone or with the\n\t\t * account. It is set for every connection of that app on the agent,\n\t\t * which share one list. Left out, the tools stay as they are.\n\t\t *\n\t\t * READ-MODIFY-WRITE, unlike `add` and `remove`, because there is no\n\t\t * route that changes one connection in place: this reads the current\n\t\t * version and publishes it again with the one connection changed. Two\n\t\t * changes racing can therefore lose one, so make them one after the\n\t\t * other. A name the agent does not have throws\n\t\t * (`connection_not_found`) before anything is sent.\n\t\t */\n\t\tchange: async (\n\t\t\tname: string,\n\t\t\tchange: (\n\t\t\t\t| { account: Account | string; endUser?: never }\n\t\t\t\t| { endUser: true; account?: never }\n\t\t\t\t| { account?: never; endUser?: never }\n\t\t\t) & { tools?: string[] | null },\n\t\t): Promise<Agent> => {\n\t\t\t// Read FRESH, never from the snapshot. A stale list sent back whole\n\t\t\t// would silently undo whatever changed since it was read.\n\t\t\tconst current = (await this.get()).connections ?? [];\n\t\t\tif (!current.some((c) => c.name === name)) {\n\t\t\t\tthrow new AgentNavaError({\n\t\t\t\t\tmessage: `This agent has no connection named \"${name}\". It has: ${\n\t\t\t\t\t\tcurrent.map((c) => c.name).join(', ') || 'none'\n\t\t\t\t\t}.`,\n\t\t\t\t\tcode: 'connection_not_found',\n\t\t\t\t\tstatus: 404,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst target = current.find((c) => c.name === name) as AgentConnection;\n\t\t\tconst whose =\n\t\t\t\tchange.endUser === true\n\t\t\t\t\t? { endUser: true as const }\n\t\t\t\t\t: change.account !== undefined\n\t\t\t\t\t\t? { account: accountIdOf(change.account) }\n\t\t\t\t\t\t: target.endUser === true\n\t\t\t\t\t\t\t? { endUser: true as const }\n\t\t\t\t\t\t\t: { account: target.account as string };\n\t\t\t// The tools as they were, unless this changes them; a change reaches\n\t\t\t// every connection of the app, which share one list.\n\t\t\tconst toolsOf = (c: AgentConnection) =>\n\t\t\t\tchange.tools === undefined\n\t\t\t\t\t? c.tools && { tools: c.tools }\n\t\t\t\t\t: c.app === target.app && change.tools !== null\n\t\t\t\t\t\t? { tools: [...change.tools] }\n\t\t\t\t\t\t: c.app === target.app\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: c.tools && { tools: c.tools };\n\t\t\t// Every other connection goes back as it was read, its tools aside:\n\t\t\t// this changes one, and must not reinterpret the rest.\n\t\t\tconst connections = current.map((c) => {\n\t\t\t\tconst { tools: _tools, ...rest } = c;\n\t\t\t\tif (c.name === name) return { name: c.name, app: c.app, ...whose, ...toolsOf(c) };\n\t\t\t\treturn { ...rest, ...toolsOf(c) };\n\t\t\t});\n\t\t\tconst data = await this.#http.post<AgentData>(`${this.base}/versions`, { connections });\n\t\t\tthis.snapshot = data;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** Remove one connection by name. Publishes a version. NEVER touches the\n\t\t * account: it stays connected for every other agent that names it, and\n\t\t * for this one if you add it back. */\n\t\tremove: async (name: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.delete<AgentData>(`${this.base}/connections/${encodeURIComponent(name)}`);\n\t\t\tif (res) this.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/**\n\t\t * Each connection with the account it uses and whether that account works\n\t\t * now: DECLARED IS NOT CONNECTED. An agent can name an account nobody has\n\t\t * finished signing in to, or one that was disconnected since, and that is\n\t\t * the state a setup checklist has to show.\n\t\t *\n\t\t * Includes what an unpublished draft adds (`inDraft`) and removes\n\t\t * (`removedInDraft`), so it matches the portal.\n\t\t */\n\t\tlist: async (): Promise<ConnectionStatus[]> => {\n\t\t\tconst res = await this.#http.get<{ connections: ConnectionStatus[] }>(`${this.base}/connections`);\n\t\t\treturn res.connections ?? [];\n\t\t},\n\t};\n\n\t// ------------------------------------------------------ secrets, agent tier\n\n\t/**\n\t * Write secret VALUES to this agent's own scope: readable by this agent and\n\t * nothing else in the workspace, overriding a workspace secret of the same\n\t * name. Writing one twice replaces the value rather than failing, because\n\t * attaching means \"this is the credential now\".\n\t *\n\t * This used to POST the whole object to `/v1/agents/:id/bindings`, which\n\t * answers only to `{kind, decl}` and records a DECLARATION, never a value. So\n\t * the call 400'd, and had it not, it would have stored the fact that a secret\n\t * is wanted rather than the secret.\n\t */\n\tasync #applyBindings(bindings: Bindings): Promise<Agent> {\n\t\tfor (const [name, value] of Object.entries(bindings.secrets ?? {})) {\n\t\t\tconst mine = (await this.#http.get<{ secrets: { id: string; name: string; agentId?: string | null }[] }>(\n\t\t\t\t'/v1/secrets',\n\t\t\t\t{ agentId: this.id },\n\t\t\t)).secrets ?? [];\n\t\t\tconst existing = mine.find((s) => s.name === name && s.agentId);\n\t\t\tif (existing) await this.#http.patch(`/v1/secrets/${encodeURIComponent(existing.id)}`, { value });\n\t\t\telse await this.#http.post('/v1/secrets', { name, value, agentId: this.id });\n\t\t}\n\t\tconst data = await this.get();\n\t\tthis.snapshot = data;\n\t\treturn new Agent(this.#http, this.id, data);\n\t}\n\n\t/**\n\t * Give this agent secret values, so every conversation with it can use them.\n\t *\n\t * ```ts\n\t * await agent.attach({ secrets: { APOLLO_API_KEY: value } });\n\t * ```\n\t *\n\t * SECRETS ONLY, since 0.44. Accounts used to be attached here, and a turn\n\t * then had to guess which of the accounts attached and owned around it to\n\t * use. The agent's setup now names them: `agent.connections.add()`. One\n\t * handed in here is refused by name (`not_a_secret`) rather than dropped.\n\t *\n\t * A stored `Secret` is not attached to an agent either. It never was: one\n\t * was accepted here and nothing was done with it. Store one for the agent\n\t * with `ws.secrets.create({ name, value, agentId })`, or attach one of your\n\t * end user's to their conversation with `conversation.attach(secret)`.\n\t */\n\tasync attach(bindings: Bindings): Promise<Agent> {\n\t\tif (!isBindings(bindings)) {\n\t\t\tthrow new AgentNavaError({\n\t\t\t\tmessage:\n\t\t\t\t\t'agent.attach takes { secrets: { NAME: value } }. Accounts go in the agent\\'s setup ' +\n\t\t\t\t\t'(agent.connections.add), and a stored secret attaches to a conversation ' +\n\t\t\t\t\t'(conversation.attach).',\n\t\t\t\tcode: 'not_a_secret',\n\t\t\t\tstatus: 400,\n\t\t\t});\n\t\t}\n\t\trefuseConnections(bindings);\n\t\treturn this.#applyBindings(bindings);\n\t}\n\n\t// ----------------------------------------------------------- conversations\n\n\t/** Start a conversation. It pins the agent's version now and keeps it.\n\t *\n\t * Nothing about accounts is passed here: the version's connections name\n\t * them, and a connection to the end user's account uses the account of\n\t * the person named by `endUserId`. */\n\tasync start(options: StartOptions): Promise<Conversation> {\n\t\tconst endUserId = requireEndUserId(options?.endUserId);\n\t\t// Resolved BEFORE the launch: a Secret is named on the wire by the\n\t\t// declared NAME it fills, and a handle addressed by id may not know its\n\t\t// own yet. Anything that is not a Secret is refused here, by name.\n\t\tconst secretIds = await secretIdsOf(secretsIn(options.attach));\n\t\tconst bindings =\n\t\t\toptions.secrets || secretIds\n\t\t\t\t? {\n\t\t\t\t\t\t...(options.secrets && { secrets: options.secrets }),\n\t\t\t\t\t\t...(options.secrets && options.secretsExpireIn !== undefined && { expiresInSeconds: options.secretsExpireIn }),\n\t\t\t\t\t\t...(secretIds && { secretIds }),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\tconst data = await this.#http.post<AgentConversationData>('/v1/conversations', {\n\t\t\tagentId: this.id,\n\t\t\t...(options.version !== undefined && { version: options.version }),\n\t\t\t...(options.parameters && { parameters: options.parameters }),\n\t\t\t// `sessionBindings`, NOT `secrets` at the top level.\n\t\t\t//\n\t\t\t// This sent them at the top level and the launch route reads\n\t\t\t// `body.sessionBindings`, so they were dropped in silence: the route's\n\t\t\t// own body type has no top-level `secrets` to read. A conversation\n\t\t\t// started with a credential began without it, and the first turn\n\t\t\t// failed somewhere else entirely.\n\t\t\t//\n\t\t\t// Third place this exact envelope mistake was found in one afternoon,\n\t\t\t// after `agent.bind` and `conversation.bind`. The shape of the bug is\n\t\t\t// always the same: the SDK agrees with itself, the route agrees with\n\t\t\t// itself, and nothing compares the two.\n\t\t\t...(bindings && { sessionBindings: bindings }),\n\t\t\t// Always present: requireEndUserId() above has already refused\n\t\t\t// anything that is not a non-empty string.\n\t\t\tuser: { id: endUserId },\n\t\t\t// ALWAYS a new conversation, even when a user is named.\n\t\t\t//\n\t\t\t// The route resumes that user's most recent conversation when it is\n\t\t\t// given an identity and not told otherwise. That default exists for\n\t\t\t// our own web app, where a page reload must not leave a trail of empty\n\t\t\t// conversations, and it is wrong for a method called `start`: a verb\n\t\t\t// that sometimes does not do the thing is worse than two verbs. It is\n\t\t\t// also ambiguous the moment a user has more than one conversation,\n\t\t\t// since it silently picks the newest.\n\t\t\t//\n\t\t\t// So the SDK does not rely on that default. `start` starts. To\n\t\t\t// continue a conversation, hold its id: `agent.conversation(id)`.\n\t\t\tfresh: true,\n\t\t});\n\t\treturn new Conversation(this.#http, data.id, data, { widgets: options.widgets === true });\n\t}\n\n\t/**\n\t * Address one of this agent's conversations by id. No request.\n\t *\n\t * Pass `{ widgets: true }` when the screen continuing it can draw widgets,\n\t * as on `start`: the choice belongs to the screen, so it is made per handle.\n\t *\n\t * Here rather than on the workspace because a conversation belongs to an\n\t * agent. You already hold the agent: it is a constant in your own code, not\n\t * a per-row value, so naming it costs nothing and the ownership is visible.\n\t *\n\t * ```ts\n\t * await ws.agent(AGENT).conversation(row.conversationId).ask('carry on');\n\t * ```\n\t */\n\tconversation(conversationId: string, options: { widgets?: boolean } = {}): Conversation {\n\t\treturn new Conversation(this.#http, conversationId, undefined, { widgets: options.widgets === true });\n\t}\n\n\t/**\n\t * This agent's most recent conversations, newest first.\n\t *\n\t * NOT ALL OF THEM, and the previous sentence here said \"every\", which was\n\t * wrong in the way that costs someone an afternoon. The server returns the\n\t * newest 50 unless you ask for more, and refuses to return more than 200 on\n\t * any single call (apps/api/src/state.ts listAgentSessions, and the Durable\n\t * Object twin beside it, clamp identically).\n\t *\n\t * For the next page, pass `before`: the `startedAt` of the last conversation\n\t * you got. `ws.endUser(id).conversations()` also pages, if the question is\n\t * one person's history rather than one agent's.\n\t *\n\t * `endUser` narrows to one of YOUR end users, which is what you want for a\n\t * \"your conversations\" list. Leaving it out returns everyone's, so leave it\n\t * out only for an operator view.\n\t *\n\t * `of: 'customers'` keeps only your end users' conversations, leaving out\n\t * the team's own (members trying the agent, jobs, webhooks); `of: 'team'`\n\t * is the other half. Left out, both.\n\t *\n\t * ```ts\n\t * const mine = await agent.conversations({ endUser: row.userId });\n\t * const shoppers = await agent.conversations({ of: 'customers', limit: 5 });\n\t * ```\n\t */\n\tasync conversations(options: { limit?: number; endUser?: string; before?: string; of?: 'customers' | 'team' } = {}): Promise<Conversation[]> {\n\t\tconst res = await this.#http.get<{ conversations: AgentConversationData[] }>('/v1/conversations', {\n\t\t\tagentId: this.id,\n\t\t\t...(options.limit !== undefined && { limit: options.limit }),\n\t\t\t// The next page, newest first: pass the `startedAt` of the last one you got.\n\t\t\t...(options.before !== undefined && { before: options.before }),\n\t\t\t// One end user's history. Sending this used to be silently ignored on\n\t\t\t// this route, which answered with everyone's conversations, so an\n\t\t\t// application rendering \"your chats\" showed somebody else's.\n\t\t\t...(options.endUser !== undefined && { end_user_id: options.endUser }),\n\t\t\t...(options.of !== undefined && { of: options.of }),\n\t\t});\n\t\treturn (res.conversations ?? []).map((c) => new Conversation(this.#http, c.id, c));\n\t}\n}\n\n/** What GET /v1/agents/:id/spec answers, as far as `draft()` reads it. */\ninterface DraftWire {\n\tisDraft?: boolean;\n\thasUnpublishedChanges?: boolean;\n\tspec: AgentFields;\n}\n\n/** GET and PUT /v1/agents/:id/website-chat-access, as sent. */\ninterface WebsiteChatWire {\n\tagentId: string;\n\taccess: WebsiteChatAccess;\n\tchatUrl: string;\n}\n\nfunction toWebsiteChat(wire: WebsiteChatWire): WebsiteChat {\n\treturn { access: wire.access, chatUrl: wire.chatUrl, embedAgentId: wire.agentId };\n}\n\n/** The end user a conversation belongs to: a non-empty string, or a thrown error before any request. */\nfunction requireEndUserId(value: unknown): string {\n\t// Checked at RUNTIME as well as in the type: a JavaScript caller, or a\n\t// TypeScript one reading a nullable column, can still pass null, a number or\n\t// ''. Each used to start an ownerless conversation that looked fine.\n\tif (typeof value === 'string' && value.trim()) return value.trim();\n\tthrow new AgentNavaError({\n\t\tmessage: 'agent.start() needs endUserId: a non-empty string naming who the conversation is for (your user id, a visitor id, or a job name).',\n\t\tcode: 'end_user_required',\n\t\tstatus: 400,\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["// An agent: a configuration you start conversations from.\n\nimport { type Account, accountIdOf } from './account.ts';\nimport { isBindings, refuseConnections, secretIdsOf, secretsIn } from './attach.ts';\nimport { Conversation } from './conversation.ts';\nimport { EndUser } from './end-user.ts';\nimport { AgentNavaError } from './errors.ts';\nimport type { Http } from './http.ts';\nimport type { Secret } from './secret.ts';\nimport { makeTurn, type Turn } from './turn.ts';\nimport { type Connection, connectionForWire } from './connection.ts';\nimport type {\n\tAgentConnection,\n\tAgentConversationData,\n\tAgentData,\n\tAgentDraft,\n\tAgentFields,\n\tBindings,\n\tConnectionStatus,\n\tFileEntry,\n\tTrigger,\n\tWebsiteChat,\n\tWebsiteChatAccess,\n\tWorkflow,\n\tWebhookDelivery,\n\tMemoryData,\n} from './types.ts';\n\nexport interface StartOptions {\n\t/**\n\t * Stored secrets to attach AS THE CONVERSATION STARTS.\n\t *\n\t * A `Secret` from `agent.endUser(id).secrets.create(...)` attaches by id, which\n\t * is the only way a stored per-person credential can reach a turn: you never\n\t * hold its value.\n\t *\n\t * ```ts\n\t * const key = await agent.endUser(row.userId).secrets.create({ name: 'ZENDESK_TOKEN', value });\n\t * const conversation = await agent.start({ endUserId: row.userId, attach: key });\n\t * ```\n\t *\n\t * Doing it here rather than with a second `conversation.attach()` call\n\t * closes a real window: between `start` and the attach, the conversation\n\t * exists and can be asked something, and it would answer without it.\n\t *\n\t * The conversation keeps a POINTER to the secret, never a copy. Replacing\n\t * its value (`secret.update(value)`) reaches the conversation on its next\n\t * tool call, and deleting the secret stops the conversation using it at\n\t * once.\n\t *\n\t * ACCOUNTS ARE NOT ATTACHED HERE any more (0.44), and one handed in is\n\t * refused. The agent's setup names the account each connection uses; for a\n\t * connection to the end user's account (`Connection.endUserAccount`), the\n\t * turn uses the account of the person this conversation is for, found by\n\t * `endUserId`. Ask\n\t * `conversation.accountsNeeded()` for the ones they have not connected yet.\n\t */\n\tattach?: Secret | Secret[];\n\n\t/**\n\t * `false` starts this conversation with end-user memory off, when the agent has\n\t * memory on: it uses nothing remembered and adds nothing. Fixed for the life of the\n\t * conversation. On by default. The team can still read the conversation, so this\n\t * is not a private chat.\n\t */\n\tmemory?: boolean;\n\n\t/**\n\t * Secret VALUES for this conversation only.\n\t *\n\t * Not `attach`, because a secret has no id you can hand over: binding one\n\t * takes the value itself. That asymmetry is in the product, not in this\n\t * naming, and hiding it behind the same word would not remove it.\n\t *\n\t * A value given here EXPIRES, after `secretsExpireIn` seconds (one hour by\n\t * default). After that the agent's tools say the sign-in expired, the\n\t * conversation lists the name in `requires`, and a turn starts with a\n\t * `credential_needed` event. Push a fresh value with\n\t * `conversation.ask(text, { secrets })` or `conversation.attach({ secrets })`.\n\t */\n\tsecrets?: Record<string, string>;\n\n\t/** How long the values in `secrets` stay usable, in seconds. Default 3600\n\t * (one hour), at most 86400 (one day). */\n\tsecretsExpireIn?: number;\n\n\t/** Pin a specific version. Defaults to the latest at the moment of starting.\n\t *\n\t * A conversation pins its version for life: publishing afterwards does not\n\t * move a conversation that is already open. The one exception is a schedule\n\t * or webhook that names the conversation (`conversationId`): each firing\n\t * moves it to the latest version, so a fix you publish reaches it. */\n\tversion?: number;\n\tparameters?: Record<string, string | number>;\n\n\t/**\n\t * WHO this conversation is for: one of YOUR users, not one of ours.\n\t *\n\t * REQUIRED, and never null. Every conversation belongs to someone. The id\n\t * is yours to choose: a user's row id, an anonymous visitor's random id, or\n\t * a name for a job such as `'nightly-report'`. Only a non-empty string is\n\t * accepted; anything else throws before a request is sent. (Decided\n\t * 2026-09-26, AGE-161: a conversation with no owner cannot be listed back to\n\t * the person it was for, and the accounts connected inside it could not be\n\t * revoked with that person.)\n\t *\n\t * It LABELS the conversation, and it decides one thing more: a connection\n\t * to the end user's account (`Connection.endUserAccount`) uses THIS\n\t * person's account for that app.\n\t * It does not decide which conversation you get, because `start` always\n\t * starts a new one. Later you can ask for everything belonging to that\n\t * person:\n\t *\n\t * ```ts\n\t * const conversation = await agent.start({ endUserId: row.userId });\n\t * const nightly = await agent.start({ endUserId: 'nightly-report' }); // a job has an id too\n\t * const theirs = await agent.conversations({ endUser: row.userId });\n\t * ```\n\t *\n\t * To CONTINUE a conversation, keep its id and address it directly. That is\n\t * the only way to continue one, on purpose:\n\t *\n\t * ```ts\n\t * await ws.agent(AGENT).conversation(row.conversationId).ask('and the rest?');\n\t * ```\n\t *\n\t * The id is YOURS and opaque to us: a row id from your database, not an\n\t * email and not something we authenticate. We never verify it, so it is\n\t * exactly as trustworthy as whatever holds your API key.\n\t *\n\t * The platform's prefixes are refused with a 400 (`end_user_reserved`):\n\t * `member:`, `visitor:` and `job:`, which name a member in the web app, an\n\t * embed visitor and work nobody is typing, and the older `webhook:`,\n\t * `builder:` and `librarian:`. Case and spacing do not matter to the check\n\t * (`MEMBER:x` is refused too). An id you chose must never be mistaken for\n\t * one of ours.\n\t *\n\t * NOT A WORKSPACE MEMBER'S ID. `ws.members()` lists the colleagues who share\n\t * this account, and their `userId` is a platform id we issued. This field is\n\t * a different namespace entirely. Both were once spelled `userId`, so\n\t * passing a member id here was a call that could not fail: there is nothing\n\t * for an end-user id to be checked against, so it just labelled the\n\t * conversation with a colleague's internal id and looked fine.\n\t */\n\tendUserId: string;\n\t/**\n\t * This conversation's screen can draw widgets: cards, tables and charts the\n\t * agent writes inside its replies, in Google's open A2UI format. Off unless set.\n\t * When on, each turn's stream carries `widget` events and `done.widgets`,\n\t * and `messages()` returns each reply's `widgets`. The agent draws only if\n\t * its own \"Answer with widgets\" setting is on (it is, unless turned off).\n\t * Draw them with `ReplyView` from `@cerebro-labs/agentnava-sdk/react`, or your own components.\n\t */\n\twidgets?: boolean;\n}\n\n/**\n * An agent you can configure and start conversations from.\n *\n * Holding one makes NO request: `ws.agent(id)` is free and the first call on it\n * is the first round trip.\n *\n * EVERY METHOD THAT CHANGES THE AGENT PUBLISHES A VERSION. There is no separate\n * save. What you do not send is inherited from the current version, which is the\n * only reason changing one field is safe: a resend that rebuilt the whole\n * configuration would drop whatever the caller forgot, and that bug has shipped\n * here four separate times.\n */\nexport class Agent {\n\treadonly id: string;\n\treadonly #http: Http;\n\tprivate snapshot?: AgentData;\n\n\tconstructor(http: Http, id: string, snapshot?: AgentData) {\n\t\tthis.#http = http;\n\t\tthis.id = id;\n\t\tif (snapshot) this.snapshot = snapshot;\n\t}\n\n\tprivate get base(): string {\n\t\treturn `/v1/agents/${encodeURIComponent(this.id)}`;\n\t}\n\n\t/** The last-known configuration, from whatever call produced this object.\n\t * Undefined on an agent you addressed by id and have not read. */\n\tget data(): AgentData | undefined {\n\t\treturn this.snapshot;\n\t}\n\n\tasync get(options: { version?: number } = {}): Promise<AgentData> {\n\t\tconst data = await this.#http.get<AgentData>(\n\t\t\tthis.base,\n\t\t\toptions.version !== undefined ? { version: options.version } : undefined,\n\t\t);\n\t\t// Only cache the CURRENT configuration. Caching a historical version would\n\t\t// make `agent.data` mean different things depending on the last call.\n\t\tif (options.version === undefined) this.snapshot = data;\n\t\treturn data;\n\t}\n\n\t/** Every version, newest first. */\n\tasync versions(): Promise<AgentData[]> {\n\t\tconst res = await this.#http.get<{ versions: AgentData[] }>(`${this.base}/versions`);\n\t\treturn res.versions ?? [];\n\t}\n\n\t/** Publish a new version with these fields changed and everything else kept.\n\t * `connections`, when sent, replaces the whole list; an `Account` in it goes\n\t * over the wire as its id (`Account.toJSON`). */\n\tasync update(fields: AgentFields): Promise<Agent> {\n\t\tconst data = await this.#http.post<AgentData>(`${this.base}/versions`, fields);\n\t\tthis.snapshot = data;\n\t\treturn new Agent(this.#http, this.id, data);\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tawait this.#http.delete(this.base);\n\t}\n\n\t// ----------------------------------------------------- draft and builder\n\n\t/**\n\t * The Agent Builder, the one behind the portal's Configure chat, working on\n\t * this agent. Most of what it changes lands in the agent's DRAFT, read with\n\t * `draft()`; two of its tools act at once, renaming the agent and importing\n\t * a website into the agent's files. The draft is the one the portal edits,\n\t * shared with anyone changing the agent there, and each message joins the\n\t * agent's one Configure conversation. So to take only part of what it writes,\n\t * run it on an agent nobody else edits (a copy made for the purpose), read\n\t * that copy's draft, publish what you keep on the real agent with\n\t * `update()`, and delete the copy. Builder messages cost no credits and\n\t * count towards a daily limit per workspace.\n\t */\n\treadonly builder = {\n\t\t/** Send the Builder one message. Await the turn for its reply, or iterate it for the events. `signal` stops waiting for it. */\n\t\task: (message: string, options: { signal?: AbortSignal } = {}): Turn =>\n\t\t\tmakeTurn(this.#http.send(`${this.base}/build`, { method: 'POST', body: { message }, ...(options.signal && { signal: options.signal }) })),\n\t};\n\n\t/** The agent as its draft holds it, and whether that draft has changes not yet published. With no draft, the live version. */\n\tasync draft(): Promise<AgentDraft> {\n\t\tconst res = await this.#http.get<DraftWire>(`${this.base}/spec`);\n\t\treturn {\n\t\t\texists: res.isDraft === true,\n\t\t\thasUnpublishedChanges: res.hasUnpublishedChanges === true,\n\t\t\tconfig: res.spec,\n\t\t};\n\t}\n\n\t// --------------------------------------------------------------- workflows\n\n\t/**\n\t * Add a workflow, or replace the one with the same name.\n\t *\n\t * Reads the current list and sends it back with this one in place, because\n\t * the API replaces the whole list: a workflow set is a list, and there is no\n\t * \"merge one item\" that does not have to decide what a duplicate name means.\n\t * Deciding it here, once, is better than every caller deciding it.\n\t */\n\tasync setWorkflow(workflow: Workflow): Promise<Agent> {\n\t\tconst current = this.snapshot?.workflows ?? (await this.get()).workflows;\n\t\tconst next = current.filter((w) => w.name !== workflow.name).concat(workflow);\n\t\treturn this.update({ workflows: next });\n\t}\n\n\tasync removeWorkflow(name: string): Promise<Agent> {\n\t\tconst current = this.snapshot?.workflows ?? (await this.get()).workflows;\n\t\treturn this.update({ workflows: current.filter((w) => w.name !== name) });\n\t}\n\n\t/** Replace the trigger list. Sending `[]` removes every trigger, including\n\t * `chat`, which is what leaves an agent that only runs on a schedule. */\n\tasync setTriggers(triggers: Trigger[]): Promise<Agent> {\n\t\treturn this.update({ triggers });\n\t}\n\n\t/**\n\t * Replace the webhook's URL and return the new one.\n\t *\n\t * The old URL stops working the moment this returns, so update the system\n\t * that calls it. Use it when a URL leaked. The webhook's `message` and who it\n\t * runs for are kept. Publishes a version. Throws `no_webhook` (409) when the\n\t * agent has no webhook trigger.\n\t */\n\tasync rotateWebhookUrl(): Promise<string> {\n\t\tconst data = await this.#http.post<AgentData>(`${this.base}/webhook/rotate`, {});\n\t\tthis.snapshot = data;\n\t\tconst webhook = data.triggers.find((t) => t.kind === 'webhook');\n\t\tif (webhook?.kind !== 'webhook' || !webhook.url) {\n\t\t\tthrow new AgentNavaError({\n\t\t\t\tmessage: 'The webhook was rotated but no URL came back. Read it with agent.get().',\n\t\t\t\tcode: 'webhook_url_missing',\n\t\t\t\tstatus: 502,\n\t\t\t});\n\t\t}\n\t\treturn webhook.url;\n\t}\n\n\t/** What the webhook received, newest first: accepted, refused (and why), a\n\t * sender's retry recognised as a repeat, or a test. Up to `limit` (200). */\n\tasync webhookDeliveries(options: { limit?: number } = {}): Promise<WebhookDelivery[]> {\n\t\tconst res = await this.#http.get<{ deliveries: WebhookDelivery[] }>(\n\t\t\t`${this.base}/webhook/deliveries`,\n\t\t\toptions.limit !== undefined ? { limit: options.limit } : undefined,\n\t\t);\n\t\treturn res.deliveries ?? [];\n\t}\n\n\t/**\n\t * Send the webhook a sample event and run the agent on it, the way a real\n\t * delivery would (the signature check aside). A real turn, charged like\n\t * one. Returns the conversation it ran in.\n\t */\n\tasync sendTestWebhook(): Promise<{ conversationId?: string }> {\n\t\tconst res = await this.#http.post<{ conversationId?: string }>(`${this.base}/webhook/test`, {});\n\t\treturn res.conversationId ? { conversationId: res.conversationId } : {};\n\t}\n\n\t// ------------------------------------------------------------ website chat\n\n\t/**\n\t * The agent's website chat: its chat page on AgentNava (the share link),\n\t * and the embed script that puts it on your website. Every agent has one\n\t * from the moment it is created, however it was made (here, through the\n\t * API, or in the portal), except an agent that needs each user's own\n\t * account or secret, which a visitor cannot bring.\n\t *\n\t * Who can chat is one setting, `access`:\n\t *\n\t * 'restricted' the default. The share link works for members of your\n\t * workspace who are signed in to AgentNava, so your team can\n\t * pass it around to test. The chat shows on another website\n\t * only when that website is on `allowedWebsites`.\n\t * 'anyone' anyone with the link can chat, and any website can show\n\t * the chat. Each visitor's turns are charged to your\n\t * workspace.\n\t *\n\t * You never need it to use the agent from code: the agent answers your API\n\t * key, and the portal's Test, as soon as it exists. Publishing a new version\n\t * moves the website chat to it, as it moves new conversations.\n\t */\n\treadonly websiteChat = {\n\t\t/** Upload a logo for the chat's look: PNG, JPG, WebP or SVG, up to 1 MB\n\t\t * (an SVG carrying script is refused). Returns its id, to set as\n\t\t * `appearance.logo` with `update()`, and where it is served. The same\n\t\t * bytes always give the same id. */\n\t\tuploadLogo: async (bytes: Uint8Array | ArrayBuffer, contentType?: string): Promise<{ id: string; url: string }> =>\n\t\t\tthis.#http.request<{ id: string; url: string }>(`${this.base}/images`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tbytes,\n\t\t\t\t...(contentType && { contentType }),\n\t\t\t}),\n\t\t/** Who can chat, the share link, and the id the embed script's\n\t\t * `data-agent-id` takes. */\n\t\tget: async (): Promise<WebsiteChat> => toWebsiteChat(\n\t\t\tawait this.#http.get<WebsiteChatWire>(`${this.base}/website-chat-access`),\n\t\t),\n\t\t/** Who can chat: 'restricted' (your workspace's signed-in members, and\n\t\t * the allowed websites) or 'anyone' (anyone with the link). */\n\t\taccess: {\n\t\t\t/** The setting as it stands. */\n\t\t\tget: async (): Promise<WebsiteChatAccess> =>\n\t\t\t\t(await this.#http.get<WebsiteChatWire>(`${this.base}/website-chat-access`)).access,\n\t\t\t/** Change it. Returns the setting as stored. Anything but 'restricted'\n\t\t\t * or 'anyone' is refused (400). */\n\t\t\tset: async (access: WebsiteChatAccess): Promise<WebsiteChatAccess> =>\n\t\t\t\t(await this.#http.put<WebsiteChatWire>(`${this.base}/website-chat-access`, { access })).access,\n\t\t},\n\t\t/** The websites a Restricted agent's chat may be shown on through the\n\t\t * embed script, where visitors chat without signing in. A domain covers\n\t\t * its subdomains; an empty list means no other website. While access is\n\t\t * 'anyone' the list is kept and not needed. */\n\t\tallowedWebsites: {\n\t\t\t/** The list as it stands. */\n\t\t\tget: async (): Promise<string[]> =>\n\t\t\t\t(await this.#http.get<{ websites: string[] }>(`${this.base}/allowed-websites`)).websites,\n\t\t\t/** Replace the list. Each entry is normalised (\"https://acme.com/page\"\n\t\t\t * becomes \"acme.com\"; \"localhost\" is allowed for testing), and one that\n\t\t\t * is not a website is refused (400). Returns the list as stored. */\n\t\t\tset: async (websites: string[]): Promise<string[]> =>\n\t\t\t\t(await this.#http.put<{ websites: string[] }>(`${this.base}/allowed-websites`, { websites })).websites,\n\t\t},\n\t};\n\n\t// --------------------------------------------------------------- knowledge\n\n\t#readKnowledgeResponse(path: string): Promise<Response> {\n\t\treturn this.#http.send(`${this.base}/knowledge/files/content`, { query: { path } });\n\t}\n\n\t/**\n\t * The agent's knowledge: files every conversation can read.\n\t *\n\t * BY REFERENCE, not by copy. A conversation reads the knowledge as it is now,\n\t * so replacing a file changes what open conversations see on their next turn.\n\t * That is the opposite of how a version works, and it is deliberate: a price\n\t * list that could not be corrected without republishing the agent would be\n\t * useless.\n\t */\n\treadonly knowledge = {\n\t\tput: async (path: string, bytes: Uint8Array | ArrayBuffer, contentType?: string): Promise<FileEntry> =>\n\t\t\tthis.#http.request<FileEntry>(`${this.base}/knowledge/files`, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\tbytes,\n\t\t\t\tquery: { path },\n\t\t\t\t...(contentType && { contentType }),\n\t\t\t}),\n\t\t/** Read an agent-owned knowledge file as bytes, without starting a turn. */\n\t\tget: async (path: string): Promise<Uint8Array> => {\n\t\t\tconst res = await this.#readKnowledgeResponse(path);\n\t\t\treturn new Uint8Array(await res.arrayBuffer());\n\t\t},\n\t\t/** Read UTF-8 text. JSON files remain text rather than being parsed. */\n\t\ttext: async (path: string): Promise<string> => {\n\t\t\tconst res = await this.#readKnowledgeResponse(path);\n\t\t\treturn res.text();\n\t\t},\n\t\tlist: async (path = '/'): Promise<FileEntry[]> => {\n\t\t\t// The route returns EVERY knowledge file for the agent; it takes no\n\t\t\t// directory parameter. Filtering here rather than sending one the server\n\t\t\t// ignores, because a call that looks filtered and is not is worse than\n\t\t\t// no filter at all. Knowledge sets are small, so one read is right.\n\t\t\tconst res = await this.#http.get<{ files: (FileEntry & { path: string })[] }>(\n\t\t\t\t`${this.base}/knowledge/files`,\n\t\t\t);\n\t\t\tconst prefix = path === '/' ? '/' : `${path.replace(/\\/+$/, '')}/`;\n\t\t\treturn (res.files ?? []).filter((f) => f.path?.startsWith(prefix));\n\t\t},\n\t\tremove: async (path: string): Promise<void> => {\n\t\t\tawait this.#http.delete(`${this.base}/knowledge/files`, { path });\n\t\t},\n\t};\n\n\t// -------------------------------------------------------- knowledge bases\n\n\t/**\n\t * The WORKSPACE knowledge bases this agent reads.\n\t *\n\t * Different from `knowledge` above, and the distinction is the whole point.\n\t * `agent.knowledge` is files this agent owns; nobody else has them. A\n\t * knowledge base is shared: one price list, attached to nine agents, corrected\n\t * in one place.\n\t *\n\t * Each attached base is mounted read-only at `/knowledge/<key>/` when a turn\n\t * starts, so the agent reads it with `ls`, `grep` and `cat`.\n\t *\n\t * ATTACHING PUBLISHES A VERSION. Which bases an agent reads is part of its\n\t * configuration, so the change is diffable and reversible like any other\n\t * configuration change. The CONTENTS of a base are not versioned: correcting\n\t * a file changes what every attached agent sees on its next turn.\n\t */\n\treadonly knowledgeBases = {\n\t\t/**\n\t\t * Attach one base by key.\n\t\t *\n\t\t * One call, not read-modify-write, deliberately. Two attaches racing\n\t\t * through a read of the current list and a write of the whole list would\n\t\t * lose one of them silently, and losing an attachment looks exactly like\n\t\t * an agent that cannot find its knowledge. The server appends.\n\t\t *\n\t\t * Attaching a key that names no base is refused, naming the key. Attaching\n\t\t * one that is already attached is a no-op, not an error.\n\t\t */\n\t\tattach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.post<AgentData>(`${this.base}/knowledge-bases`, { key });\n\t\t\tthis.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** Detach one base. The base itself and its files are untouched: this only\n\t\t * stops THIS agent from reading it. Detaching one that is not attached is\n\t\t * a no-op. */\n\t\tdetach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.delete<AgentData>(\n\t\t\t\t`${this.base}/knowledge-bases/${encodeURIComponent(key)}`,\n\t\t\t);\n\t\t\tif (res) this.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** The keys this agent currently reads, in configuration order. */\n\t\tlist: async (): Promise<string[]> => {\n\t\t\tconst current = this.snapshot ?? (await this.get());\n\t\t\treturn current.knowledgeBases ?? [];\n\t\t},\n\t};\n\n\t// -------------------------------------------------------------- databases\n\n\t/**\n\t * The WORKSPACE databases this agent may query, by key.\n\t *\n\t * Register a database first with `ws.databases.create()`, which gives it its\n\t * key; attaching names that key in this agent's configuration. Naming it is\n\t * the grant: the database answers this agent's `sql_*` calls and no agent\n\t * that does not name it, and the agent's prompt lists it by key.\n\t *\n\t * ATTACHING PUBLISHES A VERSION, like a knowledge base: which databases an\n\t * agent reaches is part of its configuration, so the change is diffable and\n\t * reversible. Detaching takes effect on the agent's next query, including in\n\t * a conversation already under way.\n\t */\n\treadonly databases = {\n\t\t/** Attach one database by key. The server appends, so two attaches cannot\n\t\t * lose each other. A key that names no database is refused; one already\n\t\t * attached is a no-op. */\n\t\tattach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.post<AgentData>(`${this.base}/databases`, { key });\n\t\t\tthis.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** Stop this agent querying one database. The database stays registered\n\t\t * for every other agent that names it. */\n\t\tdetach: async (key: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.delete<AgentData>(`${this.base}/databases/${encodeURIComponent(key)}`);\n\t\t\tif (res) this.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** The keys this agent may query, in configuration order. */\n\t\tlist: async (): Promise<string[]> => {\n\t\t\tconst current = this.snapshot ?? (await this.get());\n\t\t\treturn current.databases ?? [];\n\t\t},\n\t};\n\n\t// ------------------------------------------------------------ connections\n\n\t/**\n\t * The apps this agent uses: each a NAMED connection that says WHOSE ACCOUNT\n\t * it uses, built the way the portal's Add a connection screen asks.\n\t *\n\t * | built with | whose account | the person signs in |\n\t * |---|---|---|\n\t * | `Connection.creatorAccount(app, account)` | the agent creator's, from `ws.accounts.connect()` | once, and every conversation uses it |\n\t * | `Connection.endUserAccount(app)` | the end user's: each person talking to the agent | once each, remembered for them |\n\t *\n\t * ```ts\n\t * const { account: work } = await ws.accounts.connect({ app: App.GMAIL, nickname: 'Work' });\n\t * await agent.connections.add(Connection.creatorAccount(App.GMAIL, work, { name: 'work-gmail' }));\n\t * await agent.connections.add(Connection.endUserAccount(App.GOOGLE_CALENDAR));\n\t * ```\n\t *\n\t * A turn uses EXACTLY the account the setup names, and never another one.\n\t * There is no fallback to whichever account of that app happens to exist:\n\t * that fallback is what this replaced, and it meant reading an agent's code\n\t * told you nothing about whose mailbox it read.\n\t *\n\t * EVERY CHANGE PUBLISHES A VERSION, like `databases`: which accounts an\n\t * agent reaches is part of its configuration, so the change is diffable and\n\t * reversible. A conversation uses the connections of the version it runs.\n\t * The ACCOUNTS are not versioned: signing one in again reaches every agent\n\t * that names it, from the next turn.\n\t *\n\t * Removing a connection never touches the account. Disconnecting an account\n\t * is the other operation, on the account: `account.disconnect()`.\n\t */\n\treadonly connections = {\n\t\t/**\n\t\t * Add one connection. One call that publishes, not read-modify-write, so\n\t\t * two adds racing cannot lose each other.\n\t\t *\n\t\t * Takes what `Connection.creatorAccount()` or `Connection.endUserAccount()`\n\t\t * builds, or the same plain object. Refused, naming the problem, when the\n\t\t * name is taken on this agent (`connection_name_taken`), when the account\n\t\t * is of a different app (`account_app_mismatch`), or when no account has\n\t\t * that id (`account_not_found`).\n\t\t */\n\t\tadd: async (connection: Connection): Promise<Agent> => {\n\t\t\tconst res = await this.#http.post<AgentData>(`${this.base}/connections`, connectionForWire(connection));\n\t\t\tthis.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/**\n\t\t * Point one connection at another account: `{ account }` for the agent\n\t\t * creator's (the `Account` you hold, or its id), or `{ endUser: true }`\n\t\t * for each end user's own. The name and the app stay; an account of a\n\t\t * different app is refused (`account_app_mismatch`).\n\t\t *\n\t\t * `tools` changes which of the app's own tools the agent may use (a list\n\t\t * of ids, `[]` for none, `null` for the app's default), alone or with the\n\t\t * account. It is set for every connection of that app on the agent,\n\t\t * which share one list. Left out, the tools stay as they are.\n\t\t *\n\t\t * READ-MODIFY-WRITE, unlike `add` and `remove`, because there is no\n\t\t * route that changes one connection in place: this reads the current\n\t\t * version and publishes it again with the one connection changed. Two\n\t\t * changes racing can therefore lose one, so make them one after the\n\t\t * other. A name the agent does not have throws\n\t\t * (`connection_not_found`) before anything is sent.\n\t\t */\n\t\tchange: async (\n\t\t\tname: string,\n\t\t\tchange: (\n\t\t\t\t| { account: Account | string; endUser?: never }\n\t\t\t\t| { endUser: true; account?: never }\n\t\t\t\t| { account?: never; endUser?: never }\n\t\t\t) & { tools?: string[] | null },\n\t\t): Promise<Agent> => {\n\t\t\t// Read FRESH, never from the snapshot. A stale list sent back whole\n\t\t\t// would silently undo whatever changed since it was read.\n\t\t\tconst current = (await this.get()).connections ?? [];\n\t\t\tif (!current.some((c) => c.name === name)) {\n\t\t\t\tthrow new AgentNavaError({\n\t\t\t\t\tmessage: `This agent has no connection named \"${name}\". It has: ${\n\t\t\t\t\t\tcurrent.map((c) => c.name).join(', ') || 'none'\n\t\t\t\t\t}.`,\n\t\t\t\t\tcode: 'connection_not_found',\n\t\t\t\t\tstatus: 404,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst target = current.find((c) => c.name === name) as AgentConnection;\n\t\t\tconst whose =\n\t\t\t\tchange.endUser === true\n\t\t\t\t\t? { endUser: true as const }\n\t\t\t\t\t: change.account !== undefined\n\t\t\t\t\t\t? { account: accountIdOf(change.account) }\n\t\t\t\t\t\t: target.endUser === true\n\t\t\t\t\t\t\t? { endUser: true as const }\n\t\t\t\t\t\t\t: { account: target.account as string };\n\t\t\t// The tools as they were, unless this changes them; a change reaches\n\t\t\t// every connection of the app, which share one list.\n\t\t\tconst toolsOf = (c: AgentConnection) =>\n\t\t\t\tchange.tools === undefined\n\t\t\t\t\t? c.tools && { tools: c.tools }\n\t\t\t\t\t: c.app === target.app && change.tools !== null\n\t\t\t\t\t\t? { tools: [...change.tools] }\n\t\t\t\t\t\t: c.app === target.app\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: c.tools && { tools: c.tools };\n\t\t\t// Every other connection goes back as it was read, its tools aside:\n\t\t\t// this changes one, and must not reinterpret the rest.\n\t\t\tconst connections = current.map((c) => {\n\t\t\t\tconst { tools: _tools, ...rest } = c;\n\t\t\t\tif (c.name === name) return { name: c.name, app: c.app, ...whose, ...toolsOf(c) };\n\t\t\t\treturn { ...rest, ...toolsOf(c) };\n\t\t\t});\n\t\t\tconst data = await this.#http.post<AgentData>(`${this.base}/versions`, { connections });\n\t\t\tthis.snapshot = data;\n\t\t\treturn this;\n\t\t},\n\n\t\t/** Remove one connection by name. Publishes a version. NEVER touches the\n\t\t * account: it stays connected for every other agent that names it, and\n\t\t * for this one if you add it back. */\n\t\tremove: async (name: string): Promise<Agent> => {\n\t\t\tconst res = await this.#http.delete<AgentData>(`${this.base}/connections/${encodeURIComponent(name)}`);\n\t\t\tif (res) this.snapshot = res;\n\t\t\treturn this;\n\t\t},\n\n\t\t/**\n\t\t * Each connection with the account it uses and whether that account works\n\t\t * now: DECLARED IS NOT CONNECTED. An agent can name an account nobody has\n\t\t * finished signing in to, or one that was disconnected since, and that is\n\t\t * the state a setup checklist has to show.\n\t\t *\n\t\t * Includes what an unpublished draft adds (`inDraft`) and removes\n\t\t * (`removedInDraft`), so it matches the portal.\n\t\t */\n\t\tlist: async (): Promise<ConnectionStatus[]> => {\n\t\t\tconst res = await this.#http.get<{ connections: ConnectionStatus[] }>(`${this.base}/connections`);\n\t\t\treturn res.connections ?? [];\n\t\t},\n\t};\n\n\t// ------------------------------------------------------ secrets, agent tier\n\n\t/**\n\t * Write secret VALUES to this agent's own scope: readable by this agent and\n\t * nothing else in the workspace, overriding a workspace secret of the same\n\t * name. Writing one twice replaces the value rather than failing, because\n\t * attaching means \"this is the credential now\".\n\t *\n\t * This used to POST the whole object to `/v1/agents/:id/bindings`, which\n\t * answers only to `{kind, decl}` and records a DECLARATION, never a value. So\n\t * the call 400'd, and had it not, it would have stored the fact that a secret\n\t * is wanted rather than the secret.\n\t */\n\tasync #applyBindings(bindings: Bindings): Promise<Agent> {\n\t\tfor (const [name, value] of Object.entries(bindings.secrets ?? {})) {\n\t\t\tconst mine = (await this.#http.get<{ secrets: { id: string; name: string; agentId?: string | null }[] }>(\n\t\t\t\t'/v1/secrets',\n\t\t\t\t{ agentId: this.id },\n\t\t\t)).secrets ?? [];\n\t\t\tconst existing = mine.find((s) => s.name === name && s.agentId);\n\t\t\tif (existing) await this.#http.patch(`/v1/secrets/${encodeURIComponent(existing.id)}`, { value });\n\t\t\telse await this.#http.post('/v1/secrets', { name, value, agentId: this.id });\n\t\t}\n\t\tconst data = await this.get();\n\t\tthis.snapshot = data;\n\t\treturn new Agent(this.#http, this.id, data);\n\t}\n\n\t/**\n\t * Give this agent secret values, so every conversation with it can use them.\n\t *\n\t * ```ts\n\t * await agent.attach({ secrets: { APOLLO_API_KEY: value } });\n\t * ```\n\t *\n\t * SECRETS ONLY, since 0.44. Accounts used to be attached here, and a turn\n\t * then had to guess which of the accounts attached and owned around it to\n\t * use. The agent's setup now names them: `agent.connections.add()`. One\n\t * handed in here is refused by name (`not_a_secret`) rather than dropped.\n\t *\n\t * A stored `Secret` is not attached to an agent either. It never was: one\n\t * was accepted here and nothing was done with it. Store one for the agent\n\t * with `ws.secrets.create({ name, value, agentId })`, or attach one of your\n\t * end user's to their conversation with `conversation.attach(secret)`.\n\t */\n\tasync attach(bindings: Bindings): Promise<Agent> {\n\t\tif (!isBindings(bindings)) {\n\t\t\tthrow new AgentNavaError({\n\t\t\t\tmessage:\n\t\t\t\t\t'agent.attach takes { secrets: { NAME: value } }. Accounts go in the agent\\'s setup ' +\n\t\t\t\t\t'(agent.connections.add), and a stored secret attaches to a conversation ' +\n\t\t\t\t\t'(conversation.attach).',\n\t\t\t\tcode: 'not_a_secret',\n\t\t\t\tstatus: 400,\n\t\t\t});\n\t\t}\n\t\trefuseConnections(bindings);\n\t\treturn this.#applyBindings(bindings);\n\t}\n\n\t// ----------------------------------------------------------- conversations\n\n\t/** Start a conversation. It pins the agent's version now and keeps it.\n\t *\n\t * Nothing about accounts is passed here: the version's connections name\n\t * them, and a connection to the end user's account uses the account of\n\t * the person named by `endUserId`. */\n\tasync start(options: StartOptions): Promise<Conversation> {\n\t\tconst endUserId = requireEndUserId(options?.endUserId);\n\t\t// Resolved BEFORE the launch: a Secret is named on the wire by the\n\t\t// declared NAME it fills, and a handle addressed by id may not know its\n\t\t// own yet. Anything that is not a Secret is refused here, by name.\n\t\tconst secretIds = await secretIdsOf(secretsIn(options.attach));\n\t\tconst bindings =\n\t\t\toptions.secrets || secretIds\n\t\t\t\t? {\n\t\t\t\t\t\t...(options.secrets && { secrets: options.secrets }),\n\t\t\t\t\t\t...(options.secrets && options.secretsExpireIn !== undefined && { expiresInSeconds: options.secretsExpireIn }),\n\t\t\t\t\t\t...(secretIds && { secretIds }),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\tconst data = await this.#http.post<AgentConversationData>('/v1/conversations', {\n\t\t\tagentId: this.id,\n\t\t\t...(options.version !== undefined && { version: options.version }),\n\t\t\t...(options.parameters && { parameters: options.parameters }),\n\t\t\t...(options.memory !== undefined && { memory: options.memory }),\n\t\t\t// `sessionBindings`, NOT `secrets` at the top level.\n\t\t\t//\n\t\t\t// This sent them at the top level and the launch route reads\n\t\t\t// `body.sessionBindings`, so they were dropped in silence: the route's\n\t\t\t// own body type has no top-level `secrets` to read. A conversation\n\t\t\t// started with a credential began without it, and the first turn\n\t\t\t// failed somewhere else entirely.\n\t\t\t//\n\t\t\t// Third place this exact envelope mistake was found in one afternoon,\n\t\t\t// after `agent.bind` and `conversation.bind`. The shape of the bug is\n\t\t\t// always the same: the SDK agrees with itself, the route agrees with\n\t\t\t// itself, and nothing compares the two.\n\t\t\t...(bindings && { sessionBindings: bindings }),\n\t\t\t// Always present: requireEndUserId() above has already refused\n\t\t\t// anything that is not a non-empty string.\n\t\t\tuser: { id: endUserId },\n\t\t\t// ALWAYS a new conversation, even when a user is named.\n\t\t\t//\n\t\t\t// The route resumes that user's most recent conversation when it is\n\t\t\t// given an identity and not told otherwise. That default exists for\n\t\t\t// our own web app, where a page reload must not leave a trail of empty\n\t\t\t// conversations, and it is wrong for a method called `start`: a verb\n\t\t\t// that sometimes does not do the thing is worse than two verbs. It is\n\t\t\t// also ambiguous the moment a user has more than one conversation,\n\t\t\t// since it silently picks the newest.\n\t\t\t//\n\t\t\t// So the SDK does not rely on that default. `start` starts. To\n\t\t\t// continue a conversation, hold its id: `agent.conversation(id)`.\n\t\t\tfresh: true,\n\t\t});\n\t\treturn new Conversation(this.#http, data.id, data, { widgets: options.widgets === true });\n\t}\n\n\t/**\n\t * Address one of this agent's conversations by id. No request.\n\t *\n\t * Pass `{ widgets: true }` when the screen continuing it can draw widgets,\n\t * as on `start`: the choice belongs to the screen, so it is made per handle.\n\t *\n\t * Here rather than on the workspace because a conversation belongs to an\n\t * agent. You already hold the agent: it is a constant in your own code, not\n\t * a per-row value, so naming it costs nothing and the ownership is visible.\n\t *\n\t * ```ts\n\t * await ws.agent(AGENT).conversation(row.conversationId).ask('carry on');\n\t * ```\n\t */\n\tconversation(conversationId: string, options: { widgets?: boolean } = {}): Conversation {\n\t\treturn new Conversation(this.#http, conversationId, undefined, { widgets: options.widgets === true });\n\t}\n\n\t/**\n\t * One of YOUR end users of this agent. No request.\n\t *\n\t * The object everything about a person hangs off: their accounts, their\n\t * secrets, their conversations, and `erase()`. You say who once, here, instead\n\t * of threading an `endUserId` through each call and forgetting it in one of them.\n\t *\n\t * ```ts\n\t * const ananya = agent.endUser(row.userId);\n\t * const { url } = await ananya.accounts.connect({ app: App.GMAIL });\n\t * await ananya.conversations();\n\t * await ananya.erase();\n\t * ```\n\t *\n\t * EACH AGENT IS ITS OWN APP, WITH ITS OWN END USERS. The same id on another agent\n\t * is a different person: their own accounts, secrets and conversations. A person\n\t * using two of your agents signs in to each.\n\t *\n\t * A SCOPE, NOT A RECORD. `ws.agent(id)`, `ws.project(id)`, `ws.account(id)` and\n\t * `ws.secret(id)` point at rows we store and can list back. This points at\n\t * nothing. We do not create your end users and we cannot list them. We only write\n\t * the id you give us onto rows we already own.\n\t *\n\t * NOT a workspace member. `ws.members()` lists the colleagues who share this\n\t * account, with ids we issued and authenticate. This takes an id from YOUR\n\t * database that we store verbatim and never check.\n\t */\n\tendUser(endUserId: string): EndUser {\n\t\treturn new EndUser(this.#http, this.id, endUserId);\n\t}\n\n\t/**\n\t * Who has a memory on this agent, newest change first, at most 200 a call. For\n\t * the next page, pass `before`: the `updatedAt` of the last one you got. Someone\n\t * who asked to stop being remembered is listed, with `stopped: true`.\n\t */\n\tasync memories(options: { limit?: number; before?: string } = {}): Promise<MemoryData[]> {\n\t\tconst res = await this.#http.get<{ memories: MemoryData[] }>(`${this.base}/memories`, {\n\t\t\t...(options.limit !== undefined && { limit: options.limit }),\n\t\t\t...(options.before && { before: options.before }),\n\t\t});\n\t\treturn res.memories ?? [];\n\t}\n\n\t/**\n\t * This agent's most recent conversations, newest first.\n\t *\n\t * NOT ALL OF THEM, and the previous sentence here said \"every\", which was\n\t * wrong in the way that costs someone an afternoon. The server returns the\n\t * newest 50 unless you ask for more, and refuses to return more than 200 on\n\t * any single call (apps/api/src/state.ts listAgentSessions, and the Durable\n\t * Object twin beside it, clamp identically).\n\t *\n\t * For the next page, pass `before`: the `startedAt` of the last conversation\n\t * you got. `agent.endUser(id).conversations()` is the same list for one person.\n\t *\n\t * `endUser` narrows to one of YOUR end users, which is what you want for a\n\t * \"your conversations\" list. Leaving it out returns everyone's, so leave it\n\t * out only for an operator view.\n\t *\n\t * `of: 'customers'` keeps only your end users' conversations, leaving out\n\t * the team's own (members trying the agent, jobs, webhooks); `of: 'team'`\n\t * is the other half. Left out, both.\n\t *\n\t * ```ts\n\t * const mine = await agent.conversations({ endUser: row.userId });\n\t * const shoppers = await agent.conversations({ of: 'customers', limit: 5 });\n\t * ```\n\t */\n\tasync conversations(options: { limit?: number; endUser?: string; before?: string; of?: 'customers' | 'team' } = {}): Promise<Conversation[]> {\n\t\tconst res = await this.#http.get<{ conversations: AgentConversationData[] }>('/v1/conversations', {\n\t\t\tagentId: this.id,\n\t\t\t...(options.limit !== undefined && { limit: options.limit }),\n\t\t\t// The next page, newest first: pass the `startedAt` of the last one you got.\n\t\t\t...(options.before !== undefined && { before: options.before }),\n\t\t\t// One end user's history. Sending this used to be silently ignored on\n\t\t\t// this route, which answered with everyone's conversations, so an\n\t\t\t// application rendering \"your chats\" showed somebody else's.\n\t\t\t...(options.endUser !== undefined && { end_user_id: options.endUser }),\n\t\t\t...(options.of !== undefined && { of: options.of }),\n\t\t});\n\t\treturn (res.conversations ?? []).map((c) => new Conversation(this.#http, c.id, c));\n\t}\n}\n\n/** What GET /v1/agents/:id/spec answers, as far as `draft()` reads it. */\ninterface DraftWire {\n\tisDraft?: boolean;\n\thasUnpublishedChanges?: boolean;\n\tspec: AgentFields;\n}\n\n/** GET and PUT /v1/agents/:id/website-chat-access, as sent. */\ninterface WebsiteChatWire {\n\tagentId: string;\n\taccess: WebsiteChatAccess;\n\tchatUrl: string;\n}\n\nfunction toWebsiteChat(wire: WebsiteChatWire): WebsiteChat {\n\treturn { access: wire.access, chatUrl: wire.chatUrl, embedAgentId: wire.agentId };\n}\n\n/** The end user a conversation belongs to: a non-empty string, or a thrown error before any request. */\nfunction requireEndUserId(value: unknown): string {\n\t// Checked at RUNTIME as well as in the type: a JavaScript caller, or a\n\t// TypeScript one reading a nullable column, can still pass null, a number or\n\t// ''. Each used to start an ownerless conversation that looked fine.\n\tif (typeof value === 'string' && value.trim()) return value.trim();\n\tthrow new AgentNavaError({\n\t\tmessage: 'agent.start() needs endUserId: a non-empty string naming who the conversation is for (your user id, a visitor id, or a job name).',\n\t\tcode: 'end_user_required',\n\t\tstatus: 400,\n\t});\n}\n"]}
|