@cerebro-labs/agentnava-sdk 0.26.0 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +204 -23
  2. package/account.d.ts +154 -0
  3. package/account.js +219 -0
  4. package/account.js.map +1 -0
  5. package/agent.d.ts +217 -88
  6. package/agent.js +295 -125
  7. package/agent.js.map +1 -1
  8. package/apps.generated.d.ts +154 -0
  9. package/apps.generated.js +152 -0
  10. package/apps.generated.js.map +1 -0
  11. package/attach.d.ts +25 -26
  12. package/attach.js +53 -66
  13. package/attach.js.map +1 -1
  14. package/connection.d.ts +79 -82
  15. package/connection.js +67 -134
  16. package/connection.js.map +1 -1
  17. package/conversation.d.ts +55 -35
  18. package/conversation.js +70 -50
  19. package/conversation.js.map +1 -1
  20. package/database.d.ts +53 -0
  21. package/database.js +69 -0
  22. package/database.js.map +1 -0
  23. package/end-user.d.ts +58 -47
  24. package/end-user.js +62 -72
  25. package/end-user.js.map +1 -1
  26. package/errors.d.ts +2 -1
  27. package/errors.js +2 -1
  28. package/errors.js.map +1 -1
  29. package/http.d.ts +5 -0
  30. package/http.js +109 -8
  31. package/http.js.map +1 -1
  32. package/import-tools.d.ts +48 -33
  33. package/import-tools.js +281 -127
  34. package/import-tools.js.map +1 -1
  35. package/index.d.ts +5 -2
  36. package/index.js +9 -2
  37. package/index.js.map +1 -1
  38. package/load-agent-dir.js +28 -5
  39. package/load-agent-dir.js.map +1 -1
  40. package/package.json +5 -1
  41. package/secret.d.ts +1 -1
  42. package/secret.js +1 -1
  43. package/secret.js.map +1 -1
  44. package/templates-inline.js +1 -1
  45. package/templates-inline.js.map +1 -1
  46. package/turn.d.ts +1 -1
  47. package/turn.js +77 -29
  48. package/turn.js.map +1 -1
  49. package/types.d.ts +409 -83
  50. package/types.js.map +1 -1
  51. package/version.d.ts +1 -1
  52. package/version.js +1 -1
  53. package/version.js.map +1 -1
  54. package/workflows.d.ts +10 -0
  55. package/workflows.js +16 -0
  56. package/workflows.js.map +1 -1
  57. package/workspace.d.ts +80 -62
  58. package/workspace.js +92 -75
  59. package/workspace.js.map +1 -1
  60. package/connection-status.d.ts +0 -28
  61. package/connection-status.js +0 -61
  62. package/connection-status.js.map +0 -1
package/attach.js CHANGED
@@ -1,49 +1,27 @@
1
- // Turning the credentials a caller is holding into what the wire speaks.
1
+ // Turning the secrets a caller is holding into what the wire speaks.
2
2
  //
3
3
  // ITS OWN MODULE because `agent.ts` already imports `conversation.ts`, so a
4
4
  // helper living in either one and used by both is a cycle. Shared so that
5
5
  // `agent.start`, `agent.attach` and `conversation.attach` cannot drift: all
6
6
  // three had to agree on this shape, and when they did not, the disagreement was
7
7
  // invisible until a turn failed somewhere else.
8
- // VALUE imports, not type-only, because the split below is an `instanceof`.
9
- // Neither module imports anything but types, so this adds no cycle; `agent.ts`
10
- // and `conversation.ts` are the ones that could not import each other, which is
11
- // why this helper lives here in the first place.
12
- import { Connection as ConnectionClass } from "./connection.js";
8
+ //
9
+ // SECRETS ONLY, since 0.44. Accounts used to be attached here too, to an agent
10
+ // or to one conversation, and a turn then had to work out which of several
11
+ // attached and owned accounts to use. An agent's setup now names the account
12
+ // each connection uses (`agent.connections.add`), so there is nothing left to
13
+ // attach, and an account handed to any of these is refused by name rather than
14
+ // dropped.
15
+ import { AgentNavaError } from "./errors.js";
16
+ // A VALUE import, not type-only, because the check below is an `instanceof`.
17
+ // secret.ts imports nothing but types, so this adds no cycle.
13
18
  import { Secret } from "./secret.js";
14
19
  /**
15
- * Turn the accounts a caller is holding into the `{ provider: id }` map the
16
- * wire speaks.
20
+ * Distinguish `attach({ secrets })` from `attach(secret)`.
17
21
  *
18
- * A handle you created carries its provider already; one you addressed with
19
- * `ws.connection(id)` has to read it. That read is the only request here, and
20
- * only when it is unavoidable.
21
- */
22
- export async function providersOf(what) {
23
- if (!what)
24
- return undefined;
25
- const list = Array.isArray(what) ? what : [what];
26
- if (list.length === 0)
27
- return undefined;
28
- const out = {};
29
- for (const connection of list) {
30
- const provider = connection.provider ?? (await connection.get()).provider;
31
- // Two different accounts for one provider in a single call is a caller
32
- // mistake. A silent last-one-wins would surface much later as "the agent
33
- // read the wrong mailbox", which is expensive to trace back to here.
34
- if (out[provider] && out[provider] !== connection.id) {
35
- throw new Error(`two different ${provider} accounts were attached in one call: ${out[provider]} and ${connection.id}`);
36
- }
37
- out[provider] = connection.id;
38
- }
39
- return out;
40
- }
41
- /**
42
- * Distinguish `attach({ secrets })` from `attach(connection)`.
43
- *
44
- * Structural rather than `instanceof`, so a Connection that crossed a module
45
- * boundary or was rebuilt from JSON is still recognised. A Connection always
46
- * has an `id`; a Bindings object has only `secrets` and `connections`.
22
+ * Structural rather than `instanceof`, so a plain object built anywhere is
23
+ * still recognised. A Secret always has an `id`; a Bindings object does not.
24
+ * `connections` is recognised too, only so it can be refused by name below.
47
25
  */
48
26
  export function isBindings(what) {
49
27
  if (Array.isArray(what) || what === null || typeof what !== 'object')
@@ -51,6 +29,43 @@ export function isBindings(what) {
51
29
  const o = what;
52
30
  return !('id' in o) && ('secrets' in o || 'connections' in o);
53
31
  }
32
+ /** The words every refusal below uses, so an upgrader reads one explanation. */
33
+ const ACCOUNTS_MOVED = 'Accounts are not attached any more (0.44): name the account in the agent\'s setup with ' +
34
+ 'agent.connections.add(Connection.creatorAccount(app, account)), or use ' +
35
+ 'Connection.endUserAccount(app) so each person signs in with their own.';
36
+ /** Refuse `{ connections }`. A JavaScript caller still holding 0.43 code would
37
+ * otherwise have it dropped in silence, and the agent would answer without the
38
+ * account it was plainly given. */
39
+ export function refuseConnections(bindings) {
40
+ if ('connections' in bindings) {
41
+ throw new AgentNavaError({ message: ACCOUNTS_MOVED, code: 'not_a_secret', status: 400 });
42
+ }
43
+ }
44
+ /**
45
+ * Check that everything handed to `attach` is a Secret, and hand back the list.
46
+ *
47
+ * BY `instanceof`, not by duck-typing: a Secret addressed with `ws.secret(id)`
48
+ * has no `name` until it reads one, so "has a name" answers wrongly for exactly
49
+ * the handles that need it most.
50
+ *
51
+ * Refused rather than ignored. Silently dropping an unrecognised attachment is
52
+ * how a conversation starts without the credential its caller plainly
53
+ * attached, and the failure then appears one layer down as a tool that cannot
54
+ * authenticate.
55
+ */
56
+ export function secretsIn(what) {
57
+ const list = what === undefined ? [] : Array.isArray(what) ? what : [what];
58
+ for (const item of list) {
59
+ if (!(item instanceof Secret)) {
60
+ throw new AgentNavaError({
61
+ message: `attach takes a Secret. ${ACCOUNTS_MOVED}`,
62
+ code: 'not_a_secret',
63
+ status: 400,
64
+ });
65
+ }
66
+ }
67
+ return list;
68
+ }
54
69
  /**
55
70
  * Turn stored secrets into the `{ DECLARED_NAME: secretId }` map the wire
56
71
  * speaks.
@@ -76,8 +91,7 @@ export async function secretIdsOf(what) {
76
91
  const name = secret.name ?? (await secret.get()).name;
77
92
  // Two different secrets under one name in a single call is a caller
78
93
  // mistake, and last-one-wins would surface much later as a tool failing
79
- // on a credential that looks present. Same rule as two accounts for one
80
- // provider, for the same reason.
94
+ // on a credential that looks present.
81
95
  if (out[name] && out[name] !== secret.id) {
82
96
  throw new Error(`two different secrets were attached as ${name}: ${out[name]} and ${secret.id}`);
83
97
  }
@@ -85,31 +99,4 @@ export async function secretIdsOf(what) {
85
99
  }
86
100
  return out;
87
101
  }
88
- /**
89
- * Split a mixed list into the accounts and the credentials.
90
- *
91
- * BY `instanceof`, not by duck-typing. A `Connection` addressed with
92
- * `ws.connection(id)` has no `provider` until it reads one, and a `Secret` from
93
- * `ws.secret(id)` has no `name`, so "has a provider" and "has a name" both
94
- * answer wrongly for exactly the handles that need splitting most.
95
- */
96
- export function splitAttachments(what) {
97
- const list = what === undefined ? [] : Array.isArray(what) ? what : [what];
98
- const connections = [];
99
- const secrets = [];
100
- for (const item of list) {
101
- if (item instanceof Secret)
102
- secrets.push(item);
103
- else if (item instanceof ConnectionClass)
104
- connections.push(item);
105
- else {
106
- // Refused rather than ignored. Silently dropping an unrecognised
107
- // attachment is how a conversation starts without the credential its
108
- // caller plainly attached, and the failure then appears one layer
109
- // down as a tool that cannot authenticate.
110
- throw new Error('attach takes a Connection or a Secret');
111
- }
112
- }
113
- return { connections, secrets };
114
- }
115
102
  //# sourceMappingURL=attach.js.map
package/attach.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"attach.js","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"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","sourcesContent":["// Turning the credentials a caller is holding into what the wire speaks.\n//\n// ITS OWN MODULE because `agent.ts` already imports `conversation.ts`, so a\n// helper living in either one and used by both is a cycle. Shared so that\n// `agent.start`, `agent.attach` and `conversation.attach` cannot drift: all\n// three had to agree on this shape, and when they did not, the disagreement was\n// invisible until a turn failed somewhere else.\n\nimport type { Connection } from './connection.ts';\n// VALUE imports, not type-only, because the split below is an `instanceof`.\n// Neither module imports anything but types, so this adds no cycle; `agent.ts`\n// and `conversation.ts` are the ones that could not import each other, which is\n// why this helper lives here in the first place.\nimport { Connection as ConnectionClass } from './connection.ts';\nimport { Secret } from './secret.ts';\nimport type { Bindings } from './types.ts';\n\n/**\n * Turn the accounts a caller is holding into the `{ provider: id }` map the\n * wire speaks.\n *\n * A handle you created carries its provider already; one you addressed with\n * `ws.connection(id)` has to read it. That read is the only request here, and\n * only when it is unavoidable.\n */\nexport async function providersOf(\n\twhat: Connection | Connection[] | undefined,\n): Promise<Record<string, string> | undefined> {\n\tif (!what) return undefined;\n\tconst list = Array.isArray(what) ? what : [what];\n\tif (list.length === 0) return undefined;\n\tconst out: Record<string, string> = {};\n\tfor (const connection of list) {\n\t\tconst provider = connection.provider ?? (await connection.get()).provider;\n\t\t// Two different accounts for one provider in a single call is a caller\n\t\t// mistake. A silent last-one-wins would surface much later as \"the agent\n\t\t// read the wrong mailbox\", which is expensive to trace back to here.\n\t\tif (out[provider] && out[provider] !== connection.id) {\n\t\t\tthrow new Error(\n\t\t\t\t`two different ${provider} accounts were attached in one call: ${out[provider]} and ${connection.id}`,\n\t\t\t);\n\t\t}\n\t\tout[provider] = connection.id;\n\t}\n\treturn out;\n}\n\n/**\n * Distinguish `attach({ secrets })` from `attach(connection)`.\n *\n * Structural rather than `instanceof`, so a Connection that crossed a module\n * boundary or was rebuilt from JSON is still recognised. A Connection always\n * has an `id`; a Bindings object has only `secrets` and `connections`.\n */\nexport function isBindings(what: unknown): what is Bindings {\n\tif (Array.isArray(what) || what === null || typeof what !== 'object') return false;\n\tconst o = what as Record<string, unknown>;\n\treturn !('id' in o) && ('secrets' in o || 'connections' in o);\n}\n\n/**\n * Turn stored secrets into the `{ DECLARED_NAME: secretId }` map the wire\n * speaks.\n *\n * THE POINT OF SENDING AN ID RATHER THAN A VALUE. A secret stored against one\n * of your end users has no value you hold: the platform encrypted it when you\n * created it and never hands it back. Sending the id lets the launch resolve it\n * server-side, so attaching a credential does not require your process to have\n * read it.\n *\n * The NAME comes from the secret, because that is the name the agent declared\n * and the only thing it will look under. A handle you created carries it; one\n * you addressed by id has to read it, and that read is the only request here.\n */\nexport async function secretIdsOf(\n\twhat: Secret | Secret[] | undefined,\n): Promise<Record<string, string> | undefined> {\n\tif (!what) return undefined;\n\tconst list = Array.isArray(what) ? what : [what];\n\tif (list.length === 0) return undefined;\n\tconst out: Record<string, string> = {};\n\tfor (const secret of list) {\n\t\tconst name = secret.name ?? (await secret.get()).name;\n\t\t// Two different secrets under one name in a single call is a caller\n\t\t// mistake, and last-one-wins would surface much later as a tool failing\n\t\t// on a credential that looks present. Same rule as two accounts for one\n\t\t// provider, for the same reason.\n\t\tif (out[name] && out[name] !== secret.id) {\n\t\t\tthrow new Error(\n\t\t\t\t`two different secrets were attached as ${name}: ${out[name]} and ${secret.id}`,\n\t\t\t);\n\t\t}\n\t\tout[name] = secret.id;\n\t}\n\treturn out;\n}\n\n/** What a caller may hand to `attach`, or to `start({ attach })`. */\nexport type Attachable = Connection | Secret;\n\n/**\n * Split a mixed list into the accounts and the credentials.\n *\n * BY `instanceof`, not by duck-typing. A `Connection` addressed with\n * `ws.connection(id)` has no `provider` until it reads one, and a `Secret` from\n * `ws.secret(id)` has no `name`, so \"has a provider\" and \"has a name\" both\n * answer wrongly for exactly the handles that need splitting most.\n */\nexport function splitAttachments(\n\twhat: Attachable | Attachable[] | undefined,\n): { connections: Connection[]; secrets: Secret[] } {\n\tconst list = what === undefined ? [] : Array.isArray(what) ? what : [what];\n\tconst connections: Connection[] = [];\n\tconst secrets: Secret[] = [];\n\tfor (const item of list) {\n\t\tif (item instanceof Secret) secrets.push(item);\n\t\telse if (item instanceof ConnectionClass) connections.push(item);\n\t\telse {\n\t\t\t// Refused rather than ignored. Silently dropping an unrecognised\n\t\t\t// attachment is how a conversation starts without the credential its\n\t\t\t// caller plainly attached, and the failure then appears one layer\n\t\t\t// down as a tool that cannot authenticate.\n\t\t\tthrow new Error('attach takes a Connection or a Secret');\n\t\t}\n\t}\n\treturn { connections, secrets };\n}\n"]}
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"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","sourcesContent":["// Turning the secrets a caller is holding into what the wire speaks.\n//\n// ITS OWN MODULE because `agent.ts` already imports `conversation.ts`, so a\n// helper living in either one and used by both is a cycle. Shared so that\n// `agent.start`, `agent.attach` and `conversation.attach` cannot drift: all\n// three had to agree on this shape, and when they did not, the disagreement was\n// invisible until a turn failed somewhere else.\n//\n// SECRETS ONLY, since 0.44. Accounts used to be attached here too, to an agent\n// or to one conversation, and a turn then had to work out which of several\n// attached and owned accounts to use. An agent's setup now names the account\n// each connection uses (`agent.connections.add`), so there is nothing left to\n// attach, and an account handed to any of these is refused by name rather than\n// dropped.\n\nimport { AgentNavaError } from './errors.ts';\n// A VALUE import, not type-only, because the check below is an `instanceof`.\n// secret.ts imports nothing but types, so this adds no cycle.\nimport { Secret } from './secret.ts';\nimport type { Bindings } from './types.ts';\n\n/** What the launch and the conversation's bindings route take: secret VALUES\n * by declared name, and stored secrets by id. */\nexport interface SessionBindings {\n\tsecrets?: Record<string, string>;\n\tsecretIds?: Record<string, string>;\n}\n\n/**\n * Distinguish `attach({ secrets })` from `attach(secret)`.\n *\n * Structural rather than `instanceof`, so a plain object built anywhere is\n * still recognised. A Secret always has an `id`; a Bindings object does not.\n * `connections` is recognised too, only so it can be refused by name below.\n */\nexport function isBindings(what: unknown): what is Bindings {\n\tif (Array.isArray(what) || what === null || typeof what !== 'object') return false;\n\tconst o = what as Record<string, unknown>;\n\treturn !('id' in o) && ('secrets' in o || 'connections' in o);\n}\n\n/** The words every refusal below uses, so an upgrader reads one explanation. */\nconst ACCOUNTS_MOVED =\n\t'Accounts are not attached any more (0.44): name the account in the agent\\'s setup with ' +\n\t'agent.connections.add(Connection.creatorAccount(app, account)), or use ' +\n\t'Connection.endUserAccount(app) so each person signs in with their own.';\n\n/** Refuse `{ connections }`. A JavaScript caller still holding 0.43 code would\n * otherwise have it dropped in silence, and the agent would answer without the\n * account it was plainly given. */\nexport function refuseConnections(bindings: Bindings): void {\n\tif ('connections' in (bindings as Record<string, unknown>)) {\n\t\tthrow new AgentNavaError({ message: ACCOUNTS_MOVED, code: 'not_a_secret', status: 400 });\n\t}\n}\n\n/**\n * Check that everything handed to `attach` is a Secret, and hand back the list.\n *\n * BY `instanceof`, not by duck-typing: a Secret addressed with `ws.secret(id)`\n * has no `name` until it reads one, so \"has a name\" answers wrongly for exactly\n * the handles that need it most.\n *\n * Refused rather than ignored. Silently dropping an unrecognised attachment is\n * how a conversation starts without the credential its caller plainly\n * attached, and the failure then appears one layer down as a tool that cannot\n * authenticate.\n */\nexport function secretsIn(what: Secret | Secret[] | undefined): Secret[] {\n\tconst list = what === undefined ? [] : Array.isArray(what) ? what : [what];\n\tfor (const item of list) {\n\t\tif (!(item instanceof Secret)) {\n\t\t\tthrow new AgentNavaError({\n\t\t\t\tmessage: `attach takes a Secret. ${ACCOUNTS_MOVED}`,\n\t\t\t\tcode: 'not_a_secret',\n\t\t\t\tstatus: 400,\n\t\t\t});\n\t\t}\n\t}\n\treturn list;\n}\n\n/**\n * Turn stored secrets into the `{ DECLARED_NAME: secretId }` map the wire\n * speaks.\n *\n * THE POINT OF SENDING AN ID RATHER THAN A VALUE. A secret stored against one\n * of your end users has no value you hold: the platform encrypted it when you\n * created it and never hands it back. Sending the id lets the launch resolve it\n * server-side, so attaching a credential does not require your process to have\n * read it.\n *\n * The NAME comes from the secret, because that is the name the agent declared\n * and the only thing it will look under. A handle you created carries it; one\n * you addressed by id has to read it, and that read is the only request here.\n */\nexport async function secretIdsOf(\n\twhat: Secret | Secret[] | undefined,\n): Promise<Record<string, string> | undefined> {\n\tif (!what) return undefined;\n\tconst list = Array.isArray(what) ? what : [what];\n\tif (list.length === 0) return undefined;\n\tconst out: Record<string, string> = {};\n\tfor (const secret of list) {\n\t\tconst name = secret.name ?? (await secret.get()).name;\n\t\t// Two different secrets under one name in a single call is a caller\n\t\t// mistake, and last-one-wins would surface much later as a tool failing\n\t\t// on a credential that looks present.\n\t\tif (out[name] && out[name] !== secret.id) {\n\t\t\tthrow new Error(\n\t\t\t\t`two different secrets were attached as ${name}: ${out[name]} and ${secret.id}`,\n\t\t\t);\n\t\t}\n\t\tout[name] = secret.id;\n\t}\n\treturn out;\n}\n"]}
package/connection.d.ts CHANGED
@@ -1,89 +1,86 @@
1
- import type { Http } from './http.ts';
2
- import type { CredentialOwner, ConnectionStatus } from './types.ts';
1
+ import { type Account } from './account.ts';
2
+ import type { AppId } from './apps.generated.ts';
3
+ /** A connection that uses the AGENT CREATOR'S ACCOUNT: one account in this
4
+ * workspace, signed in once, used by every conversation. `account` is the
5
+ * `Account` you hold or its id; an `Account` goes over the wire as its id. */
6
+ export interface CreatorAccountConnection {
7
+ name?: string;
8
+ app: AppId;
9
+ account: Account | string;
10
+ endUser?: never;
11
+ }
12
+ /** A connection that uses the END USER'S ACCOUNT: each person talking to the
13
+ * agent signs in with their own, once, and it is remembered for them. */
14
+ export interface EndUserAccountConnection {
15
+ name?: string;
16
+ app: AppId;
17
+ endUser: true;
18
+ account?: never;
19
+ }
3
20
  /**
4
- * An account somebody attached, that you hold and act on.
21
+ * One connection, as you hand it to `ws.agents.create()`, `agent.update()` or
22
+ * `agent.connections.add()`: an app, and whose account.
5
23
  *
6
- * A CLASS, like `Agent`, `Conversation`, `Project` and `KnowledgeBase`. It was
7
- * briefly a bare `ConnectionStatus` with an id you carried around as a string,
8
- * which made it the only thing in this SDK you could not hold. Holding it is
9
- * the point: you create one, wait for the person to finish signing in, attach
10
- * it where it is needed, and revoke it when they leave, and each of those is a
11
- * method on the thing rather than a call with an id threaded through it.
24
+ * Build it with `Connection.creatorAccount()` or `Connection.endUserAccount()`,
25
+ * which ask the same question the portal's Add a connection screen asks. Or
26
+ * write the object yourself: `{ app, account }` or `{ app, endUser: true }`.
27
+ * Exactly one of the two, and the type says so, so a connection with both, or
28
+ * with neither, does not compile.
29
+ */
30
+ export type Connection = CreatorAccountConnection | EndUserAccountConnection;
31
+ /**
32
+ * Build the entries of an agent's setup.
12
33
  *
13
34
  * ```ts
14
- * const gmail = await ws.connections.create({ provider: 'gmail', endUserId: 'u_ananya' });
15
- * showToUser(gmail.url);
16
- * await gmail.waitUntilConnected();
17
- * await agent.attach(gmail);
35
+ * import { App, Connection } from '@cerebro-labs/agentnava-sdk';
36
+ *
37
+ * await ws.agents.create({
38
+ * name: 'Inbox helper',
39
+ * instructions: '...',
40
+ * connections: [
41
+ * Connection.creatorAccount(App.GMAIL, work, { name: 'work-gmail' }),
42
+ * Connection.creatorAccount(App.GMAIL, personal, { name: 'personal-gmail' }),
43
+ * Connection.endUserAccount(App.GOOGLE_CALENDAR),
44
+ * ],
45
+ * });
18
46
  * ```
19
47
  *
20
- * Holding one makes no request. `ws.connection(id)` is free and the first call
21
- * on it is the first round trip.
48
+ * WHY BUILDERS. Whose account a connection uses is the one decision that
49
+ * matters, and it used to be spelled `scope: 'fixed'` or `scope:
50
+ * 'conversation'`, words that answered a different question. These two names
51
+ * are the question itself, as the screen asks it: the agent creator's account,
52
+ * or the end user's account.
53
+ *
54
+ * NOT THE 0.43 `Connection`. That was a handle on a signed-in account and is
55
+ * now `Account`. This name builds an entry for an agent's setup; it holds
56
+ * nothing and makes no request.
57
+ *
58
+ * `name` tells two connections of one app apart, such as two Gmail accounts.
59
+ * Lowercase letters, digits and dashes, up to 64 characters, unique on the
60
+ * agent. Left out, it is the app's id.
22
61
  */
23
- export declare class Connection {
24
- #private;
25
- readonly id: string;
26
- /** Present when the person has not finished signing in yet: the url to send
27
- * them to. Absent on a connection you looked up rather than created. */
28
- readonly url?: string;
29
- private snapshot?;
30
- constructor(http: Http, id: string, snapshot?: ConnectionStatus, url?: string);
31
- /** Which third party this is: `'gmail'`, `'hubspot'`. Named for what the
32
- * person connecting recognises, never for whoever we buy it from. */
33
- get provider(): string | undefined;
34
- /** True once the account is attached and usable.
35
- *
36
- * Reads the last snapshot rather than the network, the same rule
37
- * `conversation.ready` follows. Undefined means this handle has not read
38
- * yet, NOT that it is disconnected. */
39
- get connected(): boolean | undefined;
40
- /** The raw status string, for the cases where the boolean is too coarse:
41
- * 'pending' and 'error' are both "not connected" and mean opposite things.
42
- * This used to be reachable only through `.data`, the whole-snapshot escape
43
- * hatch, which is a poor thing to make a caller reach for. */
44
- get status(): string | undefined;
45
- /**
46
- * Whose account it is: the workspace's, one agent's, one of YOUR END USERS',
47
- * or one conversation's.
48
- *
49
- * DERIVED HERE, from which id the row carries, rather than read off a field.
50
- * This getter used to return `snapshot.scope`, which is a different question
51
- * entirely: `scope` is what an agent DECLARED it needs ('fixed' or
52
- * 'conversation'), and the server never sent an ownership field at all. So
53
- * it answered `undefined` for every connection you looked up, and for one
54
- * you had just created it answered `'user'`, a value outside its own
55
- * declared type that only compiled through an `as never` cast.
56
- *
57
- * Most specific first: a conversation's own account, then an end user's,
58
- * then an agent's, then the workspace's.
59
- */
60
- get owner(): CredentialOwner | undefined;
61
- /** One of YOUR end users, when this account is theirs. Never a workspace
62
- * member: the two are separate id namespaces. */
63
- get endUserId(): string | null | undefined;
64
- /** The last-known state, from whatever call produced this object. */
65
- get data(): ConnectionStatus | undefined;
66
- /** Read it back. */
67
- get(): Promise<ConnectionStatus>;
68
- /**
69
- * Wait until the person has finished signing in.
70
- *
71
- * The companion to `create()`. That hands you a url; this tells you when
72
- * they are done, so you do not write a polling loop or guess which field to
73
- * believe.
74
- *
75
- * Polls rather than subscribing: there is a human in the middle, so a few
76
- * seconds costs nothing and it works from any runtime without a socket.
77
- */
78
- waitUntilConnected(options?: {
79
- timeoutMs?: number;
80
- intervalMs?: number;
81
- }): Promise<Connection>;
82
- /**
83
- * Detach the account and drop its tokens.
84
- *
85
- * This is what off-boarding calls. Every conversation using it stops acting
86
- * as that person from the next turn; nothing is retained.
87
- */
88
- revoke(): Promise<void>;
89
- }
62
+ export declare const Connection: {
63
+ /** A connection that uses the agent creator's account: the `Account` you
64
+ * hold, or its id. Produces `{ name?, app, account: '<id>' }`. */
65
+ creatorAccount(app: AppId, account: Account | string, options?: {
66
+ name?: string;
67
+ }): CreatorAccountConnection & {
68
+ account: string;
69
+ };
70
+ /** A connection that uses the end user's account: whichever account of
71
+ * `app` the person each conversation is for has connected. Produces
72
+ * `{ name?, app, endUser: true }`. */
73
+ endUserAccount(app: AppId, options?: {
74
+ name?: string;
75
+ }): EndUserAccountConnection;
76
+ };
77
+ /**
78
+ * One connection as `POST /v1/agents/:id/connections` takes it: the fields it
79
+ * declares, picked by name, and an `Account` by its id.
80
+ *
81
+ * Picked, never forwarded: a JavaScript caller holding 0.43 code could pass
82
+ * `provider` or `scope`, and those must not reach the route as if they meant
83
+ * something. Neither `account` nor `endUser` is invented here: whatever was
84
+ * given goes, and the API names the problem when it is not exactly one.
85
+ */
86
+ export declare function connectionForWire(connection: Connection): Record<string, unknown>;
package/connection.js CHANGED
@@ -1,143 +1,76 @@
1
- // A connection: a third-party account an agent acts through.
1
+ // A connection: one entry in an agent's setup. An app, and whose account.
2
+ import { accountIdOf } from "./account.js";
3
+ import { AgentNavaError } from "./errors.js";
2
4
  /**
3
- * An account somebody attached, that you hold and act on.
4
- *
5
- * A CLASS, like `Agent`, `Conversation`, `Project` and `KnowledgeBase`. It was
6
- * briefly a bare `ConnectionStatus` with an id you carried around as a string,
7
- * which made it the only thing in this SDK you could not hold. Holding it is
8
- * the point: you create one, wait for the person to finish signing in, attach
9
- * it where it is needed, and revoke it when they leave, and each of those is a
10
- * method on the thing rather than a call with an id threaded through it.
5
+ * Build the entries of an agent's setup.
11
6
  *
12
7
  * ```ts
13
- * const gmail = await ws.connections.create({ provider: 'gmail', endUserId: 'u_ananya' });
14
- * showToUser(gmail.url);
15
- * await gmail.waitUntilConnected();
16
- * await agent.attach(gmail);
8
+ * import { App, Connection } from '@cerebro-labs/agentnava-sdk';
9
+ *
10
+ * await ws.agents.create({
11
+ * name: 'Inbox helper',
12
+ * instructions: '...',
13
+ * connections: [
14
+ * Connection.creatorAccount(App.GMAIL, work, { name: 'work-gmail' }),
15
+ * Connection.creatorAccount(App.GMAIL, personal, { name: 'personal-gmail' }),
16
+ * Connection.endUserAccount(App.GOOGLE_CALENDAR),
17
+ * ],
18
+ * });
17
19
  * ```
18
20
  *
19
- * Holding one makes no request. `ws.connection(id)` is free and the first call
20
- * on it is the first round trip.
21
+ * WHY BUILDERS. Whose account a connection uses is the one decision that
22
+ * matters, and it used to be spelled `scope: 'fixed'` or `scope:
23
+ * 'conversation'`, words that answered a different question. These two names
24
+ * are the question itself, as the screen asks it: the agent creator's account,
25
+ * or the end user's account.
26
+ *
27
+ * NOT THE 0.43 `Connection`. That was a handle on a signed-in account and is
28
+ * now `Account`. This name builds an entry for an agent's setup; it holds
29
+ * nothing and makes no request.
30
+ *
31
+ * `name` tells two connections of one app apart, such as two Gmail accounts.
32
+ * Lowercase letters, digits and dashes, up to 64 characters, unique on the
33
+ * agent. Left out, it is the app's id.
21
34
  */
22
- export class Connection {
23
- id;
24
- #http;
25
- /** Present when the person has not finished signing in yet: the url to send
26
- * them to. Absent on a connection you looked up rather than created. */
27
- url;
28
- snapshot;
29
- constructor(http, id, snapshot, url) {
30
- this.#http = http;
31
- this.id = id;
32
- if (snapshot)
33
- this.snapshot = snapshot;
34
- if (url)
35
- this.url = url;
36
- }
37
- /** Which third party this is: `'gmail'`, `'hubspot'`. Named for what the
38
- * person connecting recognises, never for whoever we buy it from. */
39
- get provider() {
40
- return this.snapshot?.provider;
41
- }
42
- /** True once the account is attached and usable.
43
- *
44
- * Reads the last snapshot rather than the network, the same rule
45
- * `conversation.ready` follows. Undefined means this handle has not read
46
- * yet, NOT that it is disconnected. */
47
- get connected() {
48
- const s = this.snapshot;
49
- if (!s)
50
- return undefined;
51
- // Belt as well as braces. Every list path now normalises before
52
- // constructing a Connection, but this object is also built directly from
53
- // server payloads elsewhere, and a pass-through here meant a row that
54
- // plainly said `status: 'connected'` reported `connected === undefined`.
55
- return s.connected ?? s.status === 'connected';
56
- }
57
- /** The raw status string, for the cases where the boolean is too coarse:
58
- * 'pending' and 'error' are both "not connected" and mean opposite things.
59
- * This used to be reachable only through `.data`, the whole-snapshot escape
60
- * hatch, which is a poor thing to make a caller reach for. */
61
- get status() {
62
- return this.snapshot?.status;
63
- }
64
- /**
65
- * Whose account it is: the workspace's, one agent's, one of YOUR END USERS',
66
- * or one conversation's.
67
- *
68
- * DERIVED HERE, from which id the row carries, rather than read off a field.
69
- * This getter used to return `snapshot.scope`, which is a different question
70
- * entirely: `scope` is what an agent DECLARED it needs ('fixed' or
71
- * 'conversation'), and the server never sent an ownership field at all. So
72
- * it answered `undefined` for every connection you looked up, and for one
73
- * you had just created it answered `'user'`, a value outside its own
74
- * declared type that only compiled through an `as never` cast.
75
- *
76
- * Most specific first: a conversation's own account, then an end user's,
77
- * then an agent's, then the workspace's.
78
- */
79
- get owner() {
80
- const s = this.snapshot;
81
- if (!s)
82
- return undefined;
83
- if (s.conversationId)
84
- return 'conversation';
85
- if (s.endUserId)
86
- return 'endUser';
87
- if (s.agentId)
88
- return 'agent';
89
- return 'workspace';
90
- }
91
- /** One of YOUR end users, when this account is theirs. Never a workspace
92
- * member: the two are separate id namespaces. */
93
- get endUserId() {
94
- return this.snapshot?.endUserId;
95
- }
96
- /** The last-known state, from whatever call produced this object. */
97
- get data() {
98
- return this.snapshot;
99
- }
100
- /** Read it back. */
101
- async get() {
102
- const res = await this.#http.get('/v1/connections');
103
- const mine = (res.connections ?? []).find((c) => c.id === this.id);
104
- if (mine)
105
- this.snapshot = mine;
106
- return mine ?? { id: this.id, provider: '', declared: false, required: false, connected: false };
107
- }
108
- /**
109
- * Wait until the person has finished signing in.
110
- *
111
- * The companion to `create()`. That hands you a url; this tells you when
112
- * they are done, so you do not write a polling loop or guess which field to
113
- * believe.
114
- *
115
- * Polls rather than subscribing: there is a human in the middle, so a few
116
- * seconds costs nothing and it works from any runtime without a socket.
117
- */
118
- async waitUntilConnected(options = {}) {
119
- const timeoutMs = options.timeoutMs ?? 120_000;
120
- const intervalMs = options.intervalMs ?? 2_000;
121
- const deadline = Date.now() + timeoutMs;
122
- // Checked before the first sleep: an already-connected account should
123
- // cost one request, not one request plus an interval.
124
- let state = await this.get();
125
- while (!state.connected) {
126
- if (Date.now() >= deadline)
127
- return this;
128
- await new Promise((r) => setTimeout(r, Math.min(intervalMs, Math.max(0, deadline - Date.now()))));
129
- state = await this.get();
35
+ export const Connection = {
36
+ /** A connection that uses the agent creator's account: the `Account` you
37
+ * hold, or its id. Produces `{ name?, app, account: '<id>' }`. */
38
+ creatorAccount(app, account, options = {}) {
39
+ const id = accountIdOf(account);
40
+ // Thrown here, naming the mistake, rather than sent as a connection with
41
+ // no account for the API to refuse in other words.
42
+ if (typeof id !== 'string' || !id) {
43
+ throw new AgentNavaError({
44
+ message: `Connection.creatorAccount(${JSON.stringify(app)}, ...) needs the Account, or its id.`,
45
+ code: 'account_required',
46
+ status: 400,
47
+ });
130
48
  }
131
- return this;
132
- }
133
- /**
134
- * Detach the account and drop its tokens.
135
- *
136
- * This is what off-boarding calls. Every conversation using it stops acting
137
- * as that person from the next turn; nothing is retained.
138
- */
139
- async revoke() {
140
- await this.#http.post(`/v1/connections/${encodeURIComponent(this.id)}/revoke`);
141
- }
49
+ return { ...(options.name !== undefined && { name: options.name }), app, account: id };
50
+ },
51
+ /** A connection that uses the end user's account: whichever account of
52
+ * `app` the person each conversation is for has connected. Produces
53
+ * `{ name?, app, endUser: true }`. */
54
+ endUserAccount(app, options = {}) {
55
+ return { ...(options.name !== undefined && { name: options.name }), app, endUser: true };
56
+ },
57
+ };
58
+ /**
59
+ * One connection as `POST /v1/agents/:id/connections` takes it: the fields it
60
+ * declares, picked by name, and an `Account` by its id.
61
+ *
62
+ * Picked, never forwarded: a JavaScript caller holding 0.43 code could pass
63
+ * `provider` or `scope`, and those must not reach the route as if they meant
64
+ * something. Neither `account` nor `endUser` is invented here: whatever was
65
+ * given goes, and the API names the problem when it is not exactly one.
66
+ */
67
+ export function connectionForWire(connection) {
68
+ const c = connection;
69
+ return {
70
+ ...(c.name !== undefined && { name: c.name }),
71
+ app: c.app,
72
+ ...(c.account !== undefined && { account: accountIdOf(c.account) }),
73
+ ...(c.endUser !== undefined && { endUser: c.endUser }),
74
+ };
142
75
  }
143
76
  //# sourceMappingURL=connection.js.map