@cerebro-labs/agentnava-sdk 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +12 -0
  3. package/README.md +230 -0
  4. package/agent.d.ts +285 -0
  5. package/agent.js +394 -0
  6. package/agent.js.map +1 -0
  7. package/attach.d.ts +49 -0
  8. package/attach.js +115 -0
  9. package/attach.js.map +1 -0
  10. package/connection-status.d.ts +28 -0
  11. package/connection-status.js +61 -0
  12. package/connection-status.js.map +1 -0
  13. package/connection.d.ts +89 -0
  14. package/connection.js +143 -0
  15. package/connection.js.map +1 -0
  16. package/conversation.d.ts +177 -0
  17. package/conversation.js +348 -0
  18. package/conversation.js.map +1 -0
  19. package/end-user.d.ts +159 -0
  20. package/end-user.js +209 -0
  21. package/end-user.js.map +1 -0
  22. package/errors.d.ts +43 -0
  23. package/errors.js +55 -0
  24. package/errors.js.map +1 -0
  25. package/http.d.ts +32 -0
  26. package/http.js +192 -0
  27. package/http.js.map +1 -0
  28. package/import-tools.d.ts +282 -0
  29. package/import-tools.js +686 -0
  30. package/import-tools.js.map +1 -0
  31. package/index.d.ts +16 -0
  32. package/index.js +38 -0
  33. package/index.js.map +1 -0
  34. package/knowledge-base.d.ts +165 -0
  35. package/knowledge-base.js +274 -0
  36. package/knowledge-base.js.map +1 -0
  37. package/load-agent-dir.d.ts +17 -0
  38. package/load-agent-dir.js +192 -0
  39. package/load-agent-dir.js.map +1 -0
  40. package/package.json +32 -0
  41. package/project.d.ts +94 -0
  42. package/project.js +155 -0
  43. package/project.js.map +1 -0
  44. package/secret.d.ts +45 -0
  45. package/secret.js +69 -0
  46. package/secret.js.map +1 -0
  47. package/templates-inline.d.ts +2 -0
  48. package/templates-inline.js +16 -0
  49. package/templates-inline.js.map +1 -0
  50. package/templates.d.ts +16 -0
  51. package/templates.js +32 -0
  52. package/templates.js.map +1 -0
  53. package/turn.d.ts +11 -0
  54. package/turn.js +244 -0
  55. package/turn.js.map +1 -0
  56. package/types.d.ts +485 -0
  57. package/types.js +4 -0
  58. package/types.js.map +1 -0
  59. package/version.d.ts +1 -0
  60. package/version.js +5 -0
  61. package/version.js.map +1 -0
  62. package/workflows.d.ts +36 -0
  63. package/workflows.js +101 -0
  64. package/workflows.js.map +1 -0
  65. package/workspace.d.ts +290 -0
  66. package/workspace.js +390 -0
  67. package/workspace.js.map +1 -0
package/workflows.js ADDED
@@ -0,0 +1,101 @@
1
+ // Load workflows from a directory of markdown.
2
+ //
3
+ // Workflows are the same shape as a Claude Code SKILL.md, so an existing skills
4
+ // directory loads unchanged: frontmatter `name` and `description`, body is the
5
+ // procedure. `description` maps to `when`, because it is already the one line
6
+ // that says when the thing applies, which is exactly what `when` means.
7
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
8
+ /**
9
+ * Parse one SKILL.md-shaped document.
10
+ *
11
+ * Exported because it is the part worth testing: the frontmatter, not the
12
+ * directory walk.
13
+ */
14
+ export function parseWorkflow(source, fallbackName) {
15
+ const match = source.replace(/^/, '').match(/^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)([\s\S]*)$/);
16
+ if (!match) {
17
+ // No frontmatter: the whole file is the procedure and the filename is the
18
+ // name. Rejecting it would make the helper useless for the notes people
19
+ // actually have, and a workflow with no `when` is caught at publish with a
20
+ // message that says what to add.
21
+ return { name: fallbackName, when: '', content: source.trim() };
22
+ }
23
+ const [, frontmatter = '', body = ''] = match;
24
+ const fields = {};
25
+ for (const line of frontmatter.split(/\r?\n/)) {
26
+ const colon = line.indexOf(':');
27
+ if (colon === -1)
28
+ continue;
29
+ const key = line.slice(0, colon).trim();
30
+ let value = line.slice(colon + 1).trim();
31
+ // Deliberately NOT a YAML parser. These are two scalar fields, and pulling
32
+ // in a YAML dependency to read them would be the larger mistake. Quotes
33
+ // are stripped because people write them.
34
+ if ((value.startsWith('"') && value.endsWith('"')) ||
35
+ (value.startsWith("'") && value.endsWith("'"))) {
36
+ value = value.slice(1, -1);
37
+ }
38
+ if (key)
39
+ fields[key] = value;
40
+ }
41
+ return {
42
+ name: fields.name || fallbackName,
43
+ // `description` is Claude Code's spelling of the same idea.
44
+ when: fields.when || fields.description || '',
45
+ content: body.trim(),
46
+ };
47
+ }
48
+ /** Strip the extension and any SKILL.md wrapper directory name. */
49
+ function nameFromPath(fileName) {
50
+ return fileName.replace(/\.(md|markdown)$/i, '');
51
+ }
52
+ /**
53
+ * Read every workflow under `dir`.
54
+ *
55
+ * Handles both layouts without being told which: a flat directory of
56
+ * `*.md` files, and Claude Code's `<name>/SKILL.md` directories. Requiring the
57
+ * caller to say which one they have would be a question they should not have to
58
+ * answer about their own folder.
59
+ *
60
+ * Synchronous, because this runs at configuration time in a script, and an async
61
+ * version would infect every call site that builds an agent literal.
62
+ *
63
+ * NODE AND BUN ONLY. `node:fs` is a static import here, so a browser bundle that
64
+ * reaches this module will fail at build time rather than at run time. That is
65
+ * the right trade for a helper that exists to read a folder off disk: in a
66
+ * browser there is no folder, and the answer is to pass `workflows` directly.
67
+ * The `fs` parameter exists so this is testable without touching a disk.
68
+ */
69
+ export function loadWorkflows(dir, fs) {
70
+ const io = fs ?? { readdirSync, readFileSync, statSync };
71
+ const out = [];
72
+ for (const entry of io.readdirSync(dir).sort()) {
73
+ if (entry.startsWith('.'))
74
+ continue;
75
+ const full = `${dir.replace(/\/+$/, '')}/${entry}`;
76
+ let isDir = false;
77
+ try {
78
+ isDir = io.statSync(full).isDirectory();
79
+ }
80
+ catch {
81
+ continue;
82
+ }
83
+ if (isDir) {
84
+ // Claude Code layout: <name>/SKILL.md. The DIRECTORY is the name, which
85
+ // is why the fallback is `entry` and not 'SKILL'.
86
+ try {
87
+ out.push(parseWorkflow(io.readFileSync(`${full}/SKILL.md`, 'utf8'), entry));
88
+ }
89
+ catch {
90
+ // A directory with no SKILL.md is not a workflow. Skipping is right:
91
+ // people keep other things in these folders.
92
+ }
93
+ continue;
94
+ }
95
+ if (!/\.(md|markdown)$/i.test(entry))
96
+ continue;
97
+ out.push(parseWorkflow(io.readFileSync(full, 'utf8'), nameFromPath(entry)));
98
+ }
99
+ return out;
100
+ }
101
+ //# sourceMappingURL=workflows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflows.js","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"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","sourcesContent":["// Load workflows from a directory of markdown.\n//\n// Workflows are the same shape as a Claude Code SKILL.md, so an existing skills\n// directory loads unchanged: frontmatter `name` and `description`, body is the\n// procedure. `description` maps to `when`, because it is already the one line\n// that says when the thing applies, which is exactly what `when` means.\n\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\n\nimport type { Workflow } from './types.ts';\n\n/** The bits of `node:fs` this needs, so it can be faked in a test and so the\n * module does not break a browser bundle merely by being imported. */\ninterface FsLike {\n\treaddirSync(dir: string): string[];\n\treadFileSync(path: string, encoding: 'utf8'): string;\n\tstatSync(path: string): { isDirectory(): boolean };\n}\n\n/**\n * Parse one SKILL.md-shaped document.\n *\n * Exported because it is the part worth testing: the frontmatter, not the\n * directory walk.\n */\nexport function parseWorkflow(source: string, fallbackName: string): Workflow {\n\tconst match = source.replace(/^/, '').match(/^---\\s*\\r?\\n([\\s\\S]*?)\\r?\\n---\\s*(?:\\r?\\n|$)([\\s\\S]*)$/);\n\tif (!match) {\n\t\t// No frontmatter: the whole file is the procedure and the filename is the\n\t\t// name. Rejecting it would make the helper useless for the notes people\n\t\t// actually have, and a workflow with no `when` is caught at publish with a\n\t\t// message that says what to add.\n\t\treturn { name: fallbackName, when: '', content: source.trim() };\n\t}\n\n\tconst [, frontmatter = '', body = ''] = match;\n\tconst fields: Record<string, string> = {};\n\tfor (const line of frontmatter.split(/\\r?\\n/)) {\n\t\tconst colon = line.indexOf(':');\n\t\tif (colon === -1) continue;\n\t\tconst key = line.slice(0, colon).trim();\n\t\tlet value = line.slice(colon + 1).trim();\n\t\t// Deliberately NOT a YAML parser. These are two scalar fields, and pulling\n\t\t// in a YAML dependency to read them would be the larger mistake. Quotes\n\t\t// are stripped because people write them.\n\t\tif (\n\t\t\t(value.startsWith('\"') && value.endsWith('\"')) ||\n\t\t\t(value.startsWith(\"'\") && value.endsWith(\"'\"))\n\t\t) {\n\t\t\tvalue = value.slice(1, -1);\n\t\t}\n\t\tif (key) fields[key] = value;\n\t}\n\n\treturn {\n\t\tname: fields.name || fallbackName,\n\t\t// `description` is Claude Code's spelling of the same idea.\n\t\twhen: fields.when || fields.description || '',\n\t\tcontent: body.trim(),\n\t};\n}\n\n/** Strip the extension and any SKILL.md wrapper directory name. */\nfunction nameFromPath(fileName: string): string {\n\treturn fileName.replace(/\\.(md|markdown)$/i, '');\n}\n\n/**\n * Read every workflow under `dir`.\n *\n * Handles both layouts without being told which: a flat directory of\n * `*.md` files, and Claude Code's `<name>/SKILL.md` directories. Requiring the\n * caller to say which one they have would be a question they should not have to\n * answer about their own folder.\n *\n * Synchronous, because this runs at configuration time in a script, and an async\n * version would infect every call site that builds an agent literal.\n *\n * NODE AND BUN ONLY. `node:fs` is a static import here, so a browser bundle that\n * reaches this module will fail at build time rather than at run time. That is\n * the right trade for a helper that exists to read a folder off disk: in a\n * browser there is no folder, and the answer is to pass `workflows` directly.\n * The `fs` parameter exists so this is testable without touching a disk.\n */\nexport function loadWorkflows(dir: string, fs?: FsLike): Workflow[] {\n\tconst io: FsLike = fs ?? { readdirSync, readFileSync, statSync };\n\n\tconst out: Workflow[] = [];\n\tfor (const entry of io.readdirSync(dir).sort()) {\n\t\tif (entry.startsWith('.')) continue;\n\t\tconst full = `${dir.replace(/\\/+$/, '')}/${entry}`;\n\n\t\tlet isDir = false;\n\t\ttry {\n\t\t\tisDir = io.statSync(full).isDirectory();\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isDir) {\n\t\t\t// Claude Code layout: <name>/SKILL.md. The DIRECTORY is the name, which\n\t\t\t// is why the fallback is `entry` and not 'SKILL'.\n\t\t\ttry {\n\t\t\t\tout.push(parseWorkflow(io.readFileSync(`${full}/SKILL.md`, 'utf8'), entry));\n\t\t\t} catch {\n\t\t\t\t// A directory with no SKILL.md is not a workflow. Skipping is right:\n\t\t\t\t// people keep other things in these folders.\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!/\\.(md|markdown)$/i.test(entry)) continue;\n\t\tout.push(parseWorkflow(io.readFileSync(full, 'utf8'), nameFromPath(entry)));\n\t}\n\treturn out;\n}\n"]}
package/workspace.d.ts ADDED
@@ -0,0 +1,290 @@
1
+ import { Agent } from './agent.ts';
2
+ import { Connection } from './connection.ts';
3
+ import { type ClientOptions } from './http.ts';
4
+ import { Secret } from './secret.ts';
5
+ import { EndUser } from './end-user.ts';
6
+ import { KnowledgeBase } from './knowledge-base.ts';
7
+ import { Project } from './project.ts';
8
+ import type { AgentFields, Credits, ProjectFields, SeatPlan, WorkspaceInfo, WorkspaceMember } from './types.ts';
9
+ /**
10
+ * Your workspace: the account an API key names.
11
+ *
12
+ * ```ts
13
+ * const ws = AgentNava.getWorkspace(); // reads AGENTNAVA_API_KEY
14
+ * const agent = await ws.agents.create({ … });
15
+ * const conversation = await agent.start({ endUserId: null });
16
+ * console.log(await conversation.ask('Hello'));
17
+ * ```
18
+ *
19
+ * Constructing it makes no request. So do `ws.agent(id)`
20
+ * and `ws.project(id)`: they hand you something you can call methods on, and the
21
+ * first request happens when you call one. Fetching an object just to send it a
22
+ * message would be a round trip for nothing.
23
+ *
24
+ * A returned object is not plain JSON. Store the `id`, not the object, and use
25
+ * `ws.agent(id)` to come back in.
26
+ */
27
+ export declare class Workspace {
28
+ #private;
29
+ constructor(options?: ClientOptions);
30
+ /** The workspace itself: name, credits. */
31
+ get(): Promise<WorkspaceInfo>;
32
+ readonly agents: {
33
+ /**
34
+ * Create an agent.
35
+ *
36
+ * Supplying an `id` is optional; without one the server mints it. An id
37
+ * that already exists is a 409 rather than a quiet new version of the
38
+ * existing agent, so a typo cannot silently rewrite something else. Use
39
+ * `agent.update()` to publish a version.
40
+ */
41
+ create: (fields: AgentFields) => Promise<Agent>;
42
+ list: () => Promise<Agent[]>;
43
+ };
44
+ /**
45
+ * Secrets: values your agents USE and never see.
46
+ *
47
+ * The one way to give an agent an API key. A secret is written once, stored
48
+ * encrypted, and substituted into an HTTP tool's url, headers or body on the
49
+ * server at the moment the call goes out, so the key is never in a prompt,
50
+ * never in a transcript, and never in anything the model could repeat back.
51
+ * Reference it from a tool as `{{secrets.NAME}}`.
52
+ *
53
+ * Scope is the `agentId` argument. Left out, every agent in the workspace can
54
+ * use it; supplied, only that agent can, and it takes precedence over a
55
+ * workspace secret of the same name. Prefer the narrow one for anything that
56
+ * moves money.
57
+ *
58
+ * NOTHING READS A VALUE BACK, including you. `list` returns names and
59
+ * metadata. That is deliberate rather than an omission: a value you can read
60
+ * is a value that can end up in a log.
61
+ */
62
+ readonly secrets: {
63
+ /** Store a secret. A name that already exists at the same scope is a 409,
64
+ * rather than a silent overwrite of a key something else is using. */
65
+ create: (input: {
66
+ name: string;
67
+ value: string;
68
+ description?: string;
69
+ agentId?: string;
70
+ /** One of YOUR end users, stored verbatim. Prefer
71
+ * `ws.endUser(id).secrets.create(...)`, which fills this in for you.
72
+ *
73
+ * THIS PARAMETER WAS REMOVED AND IS BACK, and the difference is that
74
+ * it now does something. It used to be accepted, written to the
75
+ * column, and read by nothing: a credential reached a turn only by
76
+ * being ATTACHED, and a secret could not be, because binding one took
77
+ * its VALUE and not an id. A parameter that accepts a write, reports
78
+ * success and changes nothing an agent can use is a decoy, so it was
79
+ * deleted rather than left to teach somebody that per-person secrets
80
+ * worked.
81
+ *
82
+ * `conversation.attach(secret)` is the route that was missing. */
83
+ endUserId?: string;
84
+ }) => Promise<Secret>;
85
+ /** Every secret this WORKSPACE holds, values excluded. Pass `agentId` to
86
+ * include that agent's own secrets alongside the workspace ones.
87
+ *
88
+ * One of your end users' secrets is NOT in here, deliberately: ask for
89
+ * those with `ws.endUser(id).secrets.list()`. A listing that mixed them
90
+ * would be a listing you could not act on, since deleting from it would
91
+ * reach into somebody's personal credentials. */
92
+ list: (agentId?: string) => Promise<Secret[]>;
93
+ };
94
+ /**
95
+ * Connections: third-party accounts your agents act through.
96
+ *
97
+ * The account is attached in a browser by the person who owns it, so YOU
98
+ * NEVER HANDLE A TOKEN. `create` returns a url to send them to.
99
+ *
100
+ * OWNING AN ACCOUNT GRANTS NOTHING. Creating one records whose it is; a
101
+ * turn uses it only where it was ATTACHED, and the attach is a line you can
102
+ * see:
103
+ *
104
+ * | owner | created with | reaches a turn when |
105
+ * |---|---|---|
106
+ * | workspace | `ws.connections.create({ provider })` | attached |
107
+ * | agent | `ws.connections.create({ agentId })` | attached |
108
+ * | **end user** | `ws.connections.create({ endUserId })` | `conversation.attach(c)` or `agent.attach(c)` |
109
+ * | conversation | `conversation.authorize()` | that one thread |
110
+ *
111
+ * The end-user owner is what an application with its own users wants.
112
+ * Ananya connects Gmail ONCE and the account stays hers across every
113
+ * conversation, so you never send her through a sign-in again. What you do
114
+ * per conversation is attach it, which costs one line and makes the grant
115
+ * readable.
116
+ *
117
+ * An earlier version resolved her account automatically from the id on the
118
+ * conversation. It was wrong even when it picked correctly: nothing in the
119
+ * code said what the agent could reach.
120
+ *
121
+ * THE END-USER ID IS YOURS AND WE NEVER CHECK IT. It is from your database,
122
+ * not ours, and it is not a workspace member's id. Pass the wrong one and
123
+ * you file an account under the wrong person, with no error.
124
+ */
125
+ readonly connections: {
126
+ /**
127
+ * Begin connecting an account, for one of your users or for an agent.
128
+ *
129
+ * Returns a url. Send the person there; they sign in, and the account
130
+ * attaches when they finish. Poll `list` or use
131
+ * `conversation.waitUntilReady()` to know when.
132
+ */
133
+ create: (input: {
134
+ provider: string;
135
+ /** One of YOUR end users, from your own database. Omit for a shared
136
+ * account. Never a `ws.members()` id: different namespace. */
137
+ endUserId?: string;
138
+ agentId?: string;
139
+ }) => Promise<Connection>;
140
+ /** What is connected. `endUserId` narrows to one end user's accounts;
141
+ * without it you get the shared ones, which is the operator's view. */
142
+ list: (input?: {
143
+ endUserId?: string;
144
+ agentId?: string;
145
+ }) => Promise<Connection[]>;
146
+ };
147
+ /**
148
+ * Knowledge bases: named folders of files your agents read.
149
+ *
150
+ * Called `knowledgeBases`, not `knowledge`, on purpose. `agent.knowledge`
151
+ * already means one agent's FILES, and a `ws.knowledge.list()` returning
152
+ * bases rather than files would be the same word meaning two different things
153
+ * one level apart. The longer name is worth it.
154
+ */
155
+ readonly knowledgeBases: {
156
+ /**
157
+ * Create a base. The KEY is what agent configurations store, so pick
158
+ * something readable: `product-docs`, not `kb1`.
159
+ *
160
+ * A key already in use is an error rather than a silent overwrite, because
161
+ * the base it would replace may be one several agents already read from.
162
+ */
163
+ create: (fields: {
164
+ key: string;
165
+ name?: string;
166
+ description?: string;
167
+ }) => Promise<KnowledgeBase>;
168
+ /** Every base in this workspace. No pagination: bases are few, and a
169
+ * workspace with hundreds of them has a naming problem rather than a
170
+ * paging one. */
171
+ list: () => Promise<KnowledgeBase[]>;
172
+ /** One base, by key. Returns a HANDLE you act through: `kb.files.put(...)`,
173
+ * `kb.attachments()`, `kb.delete()`. */
174
+ get: (key: string) => Promise<KnowledgeBase>;
175
+ };
176
+ readonly projects: {
177
+ create: (fields: ProjectFields) => Promise<Project>;
178
+ list: () => Promise<Project[]>;
179
+ };
180
+ /**
181
+ * Who is on this workspace, and what each of them may do.
182
+ *
183
+ * ```ts
184
+ * for (const m of await ws.members()) {
185
+ * console.log(m.email, m.role);
186
+ * }
187
+ * ```
188
+ */
189
+ members(): Promise<WorkspaceMember[]>;
190
+ /**
191
+ * What credit is left, and roughly how many turns that buys.
192
+ *
193
+ * The turn counts are the useful half: a balance in credits is not something
194
+ * anybody can act on without knowing what a turn costs.
195
+ *
196
+ * ```ts
197
+ * const { credits, standardTurnsRemaining } = await ws.credits();
198
+ * if (standardTurnsRemaining < 100) await topUpSomehow();
199
+ * ```
200
+ */
201
+ credits(): Promise<Credits>;
202
+ /**
203
+ * Which seat plan this workspace is on.
204
+ *
205
+ * Seats and credits are separate purchases: this says how many people may
206
+ * share the account, not how much work they may do. Returns null when the
207
+ * server cannot say, rather than guessing at the free plan, because "we do
208
+ * not know" and "you are on the cheapest one" are different answers.
209
+ */
210
+ plan(): Promise<SeatPlan | null>;
211
+ /** Address an agent you already have the id of. No request. */
212
+ agent(agentId: string): Agent;
213
+ /** Address a project you already have the id of. No request. */
214
+ project(projectId: string): Project;
215
+ /**
216
+ * One of YOUR end users. No request.
217
+ *
218
+ * The object everything about a person hangs off: their connections, their
219
+ * conversations across every agent. You say who once, here, instead of
220
+ * threading an `endUserId` through each call and forgetting it in one of
221
+ * them.
222
+ *
223
+ * ```ts
224
+ * const ananya = ws.endUser(row.userId);
225
+ * await ananya.connections.create({ provider: 'gmail' });
226
+ * await ananya.conversations();
227
+ * ```
228
+ *
229
+ * A SCOPE, NOT A RECORD. Every other addresser here points at a row we
230
+ * store and can list back: `ws.agent(id)`, `ws.project(id)`,
231
+ * `ws.connection(id)`, `ws.secret(id)`, each with its own
232
+ * `ws.<plural>.create/list`. This one points at nothing. We do not create
233
+ * your end users, we cannot list them, and there is no `ws.endUsers`
234
+ * collection anywhere in this SDK. We only write the id you give us onto
235
+ * rows we already own.
236
+ *
237
+ * So this does not fetch a person. It hands you the same workspace,
238
+ * narrowed to one of your people. If a `ws.endUsers` ever appears here,
239
+ * something has started claiming to own them.
240
+ *
241
+ * It hangs off the workspace because a an end-user id is MEANINGLESS WITHOUT
242
+ * ONE: uniqueness is (workspace_id, end_user_id, provider), so two of our
243
+ * own customers can each have a person called `user_1` without colliding.
244
+ *
245
+ * NOT `ws.user()`, and not a workspace member. `ws.members()` lists the
246
+ * colleagues who share this account, with ids we issued and authenticate.
247
+ * This takes an id from YOUR database that we store verbatim and never
248
+ * check. Passing one where the other belongs used to be a call that could
249
+ * not fail, because there is nothing for an end-user id to fail against.
250
+ */
251
+ endUser(endUserId: string): EndUser;
252
+ /** Address a connection you already have the id of. No request.
253
+ *
254
+ * The sibling of `ws.agent(id)`. A connection belongs to the workspace, not
255
+ * to an agent: the same account can be the agent's shared one, one person's
256
+ * or one conversation's, and which it is lives on the row rather than in
257
+ * where you reached it from. */
258
+ connection(connectionId: string): Connection;
259
+ /** Address a secret you already have the id of. No request. */
260
+ secret(secretId: string): Secret;
261
+ /** Address a knowledge base you already know the key of. No request.
262
+ *
263
+ * The sibling of `agent()` and `project()`, and it was the
264
+ * one missing: reaching a base meant `knowledgeBases.get(key)`, which costs a
265
+ * round trip to learn something the caller already knew. Use `get()` when you
266
+ * want the base's RECORD; use this when you only want to put a file in it. */
267
+ knowledgeBase(key: string): KnowledgeBase;
268
+ }
269
+ /**
270
+ * The way in.
271
+ *
272
+ * ```ts
273
+ * import { AgentNava } from '@cerebro-labs/agentnava-sdk';
274
+ *
275
+ * const ws = AgentNava.getWorkspace(); // reads AGENTNAVA_API_KEY
276
+ * const ws = AgentNava.getWorkspace('ank_...'); // or pass the key
277
+ * ```
278
+ *
279
+ * IT MAKES NO REQUEST. `get` here means "hand me the workspace this key names",
280
+ * not "go and fetch it"; the first round trip happens on the first method you
281
+ * call. `ws.get()` is the one that actually reads the record.
282
+ *
283
+ * An API key belongs to exactly one workspace, so there is nothing to select
284
+ * and no workspace argument anywhere in this SDK. This does not CREATE a
285
+ * workspace either: the key already names one, and creating them is not
286
+ * something an API key may do (see "the account is read-only").
287
+ */
288
+ export declare const AgentNava: {
289
+ getWorkspace(options?: string | ClientOptions): Workspace;
290
+ };