@alfe.ai/agent-api-client 0.3.0 → 0.4.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 +16 -0
- package/dist/index.cjs +139 -0
- package/dist/index.d.cts +152 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +152 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +139 -0
- package/dist/index.js.map +1 -1
- package/package.json +10 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @alfe.ai/agent-api-client — Agent self-service API client.\n *\n * Used by agents calling /agent/ endpoints. The agent authenticates\n * with its API key — the backend resolves agentId + tenantId from the token.\n * No agent ID needed in paths or config.\n */\n\nexport type {\n IntegrationInstall,\n IntegrationConfigResult,\n RegistryEntry,\n IntegrationConfigSchemaField,\n SecretScope,\n EncryptedEnvelopeV1,\n SecretMetadata,\n SecretAggregate,\n Field,\n FieldFormat,\n FieldSensitivity,\n FieldView,\n FieldEnvelope,\n SecretCategory,\n ChangelogEntry,\n ChangelogAction,\n ChangelogActor,\n ScopeInfo,\n GeneratedDataKey,\n} from \"@alfe/types\";\n\nimport type {\n IntegrationInstall,\n IntegrationConfigResult,\n RegistryEntry,\n SecretScope,\n EncryptedEnvelopeV1,\n SecretMetadata,\n SecretAggregate,\n FieldFormat,\n FieldSensitivity,\n FieldEnvelope,\n SecretCategory,\n ChangelogEntry,\n ScopeInfo,\n GeneratedDataKey,\n} from \"@alfe/types\";\n\nexport interface AgentApiClientConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n// ─── Sync types ──────────────────────────────────────────\n\nexport interface SyncAgentInfo {\n agentId: string;\n tenantId: string;\n displayName: string;\n s3Prefix: string;\n status: \"stale\" | \"syncing\" | \"synced\";\n fileCount?: number;\n totalSize?: number;\n lastSync?: string;\n}\n\nexport interface SyncManifestEntry {\n hash: string;\n size: number;\n modified: string;\n etag?: string;\n storageClass?: string;\n compressed?: boolean;\n}\n\nexport interface SyncManifest {\n version: 1;\n agentId: string;\n lastSync: string;\n files: Record<string, SyncManifestEntry>;\n}\n\nexport interface SyncPresignedUrl {\n path: string;\n url: string;\n expiresAt: string;\n}\n\nexport interface SyncConfirmedUpload {\n filePath: string;\n hash: string;\n size: number;\n storageClass: \"STANDARD\" | \"GLACIER_IR\";\n syncedAt: string;\n}\n\nexport interface SyncReconstructFile {\n path: string;\n size: number;\n url: string;\n storageClass?: string;\n compressed?: boolean;\n}\n\nexport interface SyncReconstructBundle {\n agentId: string;\n mode: \"full\" | \"active\" | \"memory\";\n fileCount: number;\n totalSize: number;\n files: SyncReconstructFile[];\n expiresAt: string;\n}\n\nexport interface SyncAgentStats {\n agentId: string;\n standardBytes: number;\n glacierBytes: number;\n fileCount: number;\n lastSyncAt: string | null;\n}\n\nexport interface SyncFileEntry {\n filePath: string;\n size: number;\n modified: string;\n contentHash: string;\n storageClass?: string;\n compressed?: boolean;\n}\n\nexport interface SyncSessionEntry {\n sessionId: string;\n size: number;\n lastModified: string;\n storageClass?: string;\n isArchived: boolean;\n}\n\nexport interface SyncSessionContent {\n sessionId: string;\n content: string;\n compressed: boolean;\n}\n\nexport interface SharedFileEntry {\n filePath: string;\n fileName: string;\n size: number;\n contentType?: string;\n}\n\n// ─── Knowledge resource types (org/team/project) ──────────\n//\n// Scoped, searchable knowledge ABOUT a thing being worked on. The system\n// of record is `services/org` (docs/facts/profile + the membership gate);\n// `services/knowledge` is a pure RAG projection (vector search). `scopeId`\n// for the `org` scope is the tenantId (covers personal + org tenants\n// identically) — agents discover it via `listScopes()`.\n\nexport type KnowledgeScopeType = \"org\" | \"team\" | \"project\";\n\nexport interface KnowledgeScope {\n scopeType: KnowledgeScopeType;\n scopeId: string;\n name: string;\n}\n\nexport interface KnowledgeSearchHit {\n id: string;\n text: string;\n /** Normalized relevance in (0,1]; higher = closer. */\n score: number;\n scopeType: KnowledgeScopeType;\n scopeId: string;\n source: \"doc\" | \"fact\";\n /** Present when `source === \"doc\"` — the canonical file under shared/<scope>/. */\n filePath?: string;\n /** Present when `source === \"fact\"`. */\n factId?: string;\n}\n\nexport interface KnowledgeSearchResult {\n results: KnowledgeSearchHit[];\n /** True when fan-out breadth was capped (more member scopes than the cap). */\n truncatedScopes: boolean;\n}\n\nexport interface KnowledgeProfileLink {\n label: string;\n url: string;\n}\n\nexport interface KnowledgeProfile {\n scopeType: KnowledgeScopeType;\n scopeId: string;\n about: string | null;\n description: string | null;\n links: KnowledgeProfileLink[];\n updatedAt: string | null;\n updatedBy: string | null;\n}\n\nexport interface KnowledgeFact {\n factId: string;\n scopeType: KnowledgeScopeType;\n scopeId: string;\n text: string;\n sourceType: string;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface KnowledgeDoc {\n filePath: string;\n fileName: string;\n contentType?: string;\n size: number;\n uploadedBy?: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Encode each path segment but keep the `/` separators — `encodeURIComponent`\n * would escape the slashes too, breaking greedy proxy routes.\n */\nfunction encodeFilePath(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\n// Per-request budget. Covers cold-start chains (authorizer + handler\n// + downstream OAuth provider) with headroom; the worst legitimate\n// path observed is ~7s (cold Lambda + Atlassian token exchange).\nconst REQUEST_TIMEOUT_MS = 20_000;\n// Statuses worth one retry — API GW synthesizes 500 on authorizer\n// timeout, 502/503/504 cover upstream cold-start and LB transients.\nconst RETRYABLE_STATUS = new Set([500, 502, 503, 504]);\nconst RETRY_DELAY_MS = 500;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction isRetryableNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // AbortSignal.timeout() rejects with TimeoutError (DOMException).\n if (err.name === \"TimeoutError\" || err.name === \"AbortError\") return true;\n // undici wraps socket-level failures (stale keep-alive connections,\n // ECONNRESET on reused pool entries) in a TypeError \"fetch failed\"\n // with the cause attached.\n if (err.name === \"TypeError\") return true;\n return false;\n}\n\nexport class AgentApiClient {\n private readonly apiKey: string;\n private readonly apiUrl: string;\n\n constructor(config: AgentApiClientConfig) {\n this.apiKey = config.apiKey;\n this.apiUrl = config.apiUrl;\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n const url = `${this.apiUrl}${path}`;\n const headers = new Headers(options?.headers);\n headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n headers.set(\"Content-Type\", \"application/json\");\n\n // One retry on transient failures. Long-running daemon processes\n // (MCP proxies, the gateway) call this client after multi-hour\n // idle gaps; the first attempt then rides a cold path end-to-end —\n // including the API Gateway Lambda authorizer, which API GW\n // hard-caps at 10s and surfaces as a synthetic 500 WITHOUT ever\n // invoking the route handler. Observed on QA Tester (dev,\n // 2026-06-11): atlassian-mcp-proxy token refreshes failed ~59%\n // of attempts with an 11-12s hang then 500; the same request\n // re-issued seconds later succeeded in ~1.5s. A single retry\n // converts that failure mode into a slow success.\n //\n // Retrying POSTs is safe here: the 500 fires before the handler\n // runs (authorizer timeout), and the one rotating-token endpoint\n // (connect/{provider}/refresh) reads its refresh token from the\n // DB per call, so a duplicate invocation re-rotates cleanly\n // rather than corrupting state.\n let lastError: unknown;\n for (let attempt = 1; attempt <= 2; attempt++) {\n try {\n const res = await fetch(url, {\n ...options,\n headers,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n\n if (!res.ok) {\n await res.text(); // drain body\n const error = new Error(`Agent API request failed (${String(res.status)})`);\n if (attempt === 1 && RETRYABLE_STATUS.has(res.status)) {\n lastError = error;\n await sleep(RETRY_DELAY_MS);\n continue;\n }\n throw error;\n }\n\n const body = (await res.json()) as { data: T };\n return body.data;\n } catch (err) {\n if (attempt === 1 && isRetryableNetworkError(err)) {\n lastError = err;\n await sleep(RETRY_DELAY_MS);\n continue;\n }\n throw err;\n }\n }\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n }\n\n // ─── Sync ────────────────────────────────────────────────\n //\n // Workspace backup. The agent only ever calls /agent/sync/* — file\n // bytes go to/from S3 via presigned URLs (S3 fetch is the one\n // legitimate raw-fetch in a plugin). Dashboard editing uses the\n // user-API at /sync/agents/{agentId}/* and is not exposed here.\n\n async syncRegister(args?: { displayName?: string }): Promise<{ agent: SyncAgentInfo }> {\n return this.request(\"/agent/sync/register\", {\n method: \"POST\",\n body: JSON.stringify(args ?? {}),\n });\n }\n\n async syncGetManifest(): Promise<SyncManifest> {\n return this.request(\"/agent/sync/manifest\");\n }\n\n async syncPresign(args: {\n files: { path: string; operation: \"put\" | \"get\"; contentType?: string }[];\n }): Promise<{ urls: SyncPresignedUrl[] }> {\n return this.request(\"/agent/sync/presign\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncConfirmUpload(args: {\n filePath: string;\n hash: string;\n size: number;\n storageClass?: \"STANDARD\" | \"GLACIER_IR\";\n }): Promise<SyncConfirmedUpload> {\n return this.request(\"/agent/sync/confirm\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncReconstruct(args: {\n mode: \"full\" | \"active\" | \"memory\";\n }): Promise<SyncReconstructBundle> {\n return this.request(\"/agent/sync/reconstruct\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncGetStats(): Promise<SyncAgentStats> {\n return this.request(\"/agent/sync/stats\");\n }\n\n async syncListFiles(args?: { prefix?: string }): Promise<{ files: SyncFileEntry[] }> {\n const qs = new URLSearchParams();\n if (args?.prefix) qs.set(\"prefix\", args.prefix);\n const query = qs.toString();\n return this.request(`/agent/sync/files${query ? `?${query}` : \"\"}`);\n }\n\n async syncListSessions(): Promise<{ sessions: SyncSessionEntry[] }> {\n return this.request(\"/agent/sync/sessions\");\n }\n\n async syncGetSession(sessionId: string): Promise<SyncSessionContent> {\n return this.request(`/agent/sync/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async syncDeleteFile(filePath: string): Promise<{ removed: boolean }> {\n return this.request(`/agent/sync/files/${encodeFilePath(filePath)}`, {\n method: \"DELETE\",\n });\n }\n\n // ─── Shared (org/team/project) files ─────────────────────\n //\n // Used by the sync plugin's shared-sync engine to mirror org-scoped\n // files into the agent's `shared/` directory. Routes live in services/org.\n\n async sharedListFiles(args: {\n scope: \"org\" | \"team\" | \"project\";\n scopeId: string;\n }): Promise<{ files: SharedFileEntry[]; nextCursor: string | null }> {\n return this.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}`,\n );\n }\n\n async sharedDownloadUrl(args: {\n scope: \"org\" | \"team\" | \"project\";\n scopeId: string;\n filePath: string;\n }): Promise<{ downloadUrl: string; expiresIn: number }> {\n return this.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/download/${encodeFilePath(args.filePath)}`,\n );\n }\n\n async listIntegrations(): Promise<IntegrationInstall[]> {\n return this.request<IntegrationInstall[]>(\"/agent/integrations\");\n }\n\n async getIntegrationConfig(integrationId: string): Promise<IntegrationConfigResult> {\n return this.request<IntegrationConfigResult>(\n `/agent/integrations/${encodeURIComponent(integrationId)}/config`,\n );\n }\n\n async updateIntegrationConfig(\n integrationId: string,\n config: Record<string, unknown>,\n ): Promise<void> {\n await this.request<unknown>(\n `/agent/integrations/${encodeURIComponent(integrationId)}`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ config }),\n },\n );\n }\n\n async installIntegration(\n integrationId: string,\n options?: { version?: string; config?: Record<string, unknown> },\n ): Promise<IntegrationInstall> {\n return this.request<IntegrationInstall>(\"/agent/integrations\", {\n method: \"POST\",\n body: JSON.stringify({\n integrationId,\n version: options?.version,\n config: options?.config,\n }),\n });\n }\n\n async removeIntegration(integrationId: string): Promise<IntegrationInstall> {\n return this.request<IntegrationInstall>(\n `/agent/integrations/${encodeURIComponent(integrationId)}`,\n { method: \"DELETE\" },\n );\n }\n\n async getOAuthUrl(\n provider: string,\n scopes?: string[],\n ): Promise<{ url: string; provider: string; expiresIn: number }> {\n const params = new URLSearchParams({ provider });\n if (scopes?.length) params.set(\"scopes\", scopes.join(\",\"));\n return this.request(`/agent/integrations/oauth/url?${params.toString()}`);\n }\n\n async getOAuthStatus(\n provider: string,\n ): Promise<{ provider: string; connected: boolean; config?: Record<string, string> }> {\n return this.request(\n `/agent/integrations/oauth/status?provider=${encodeURIComponent(provider)}`,\n );\n }\n\n async getRegistry(): Promise<{ integrations: RegistryEntry[] }> {\n return this.request<{ integrations: RegistryEntry[] }>(\"/integrations/registry\");\n }\n\n /**\n * Returns every connected Google account for the agent. Multi-account by\n * design — the openclaw-google plugin requires the LLM to pass `email`\n * explicitly to `google_run_command` so an account is always selected\n * deliberately.\n *\n * 2026-05-14 (connections-redesign PR 1): the legacy flat shape (`email`,\n * `refreshToken`, `accessToken`, etc., populated from the default account)\n * is gone. Iterate over `accounts`.\n */\n async getGoogleCredentials(): Promise<{\n accounts: {\n email: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n displayName?: string;\n connectedAt?: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n accountIdentifier: string;\n accessToken?: string;\n refreshToken?: string;\n clientId?: string;\n clientSecret?: string;\n workspaceDomain?: string;\n displayName?: string | null;\n connectedAt?: string;\n }[];\n }>(\"/agent/connect/google/accounts\");\n\n return {\n accounts: raw.accounts.map((a) => ({\n email: a.accountIdentifier,\n refreshToken: a.refreshToken ?? \"\",\n clientId: a.clientId ?? \"\",\n clientSecret: a.clientSecret ?? \"\",\n displayName: a.displayName ?? undefined,\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n async disconnectGoogleAccount(email: string): Promise<{\n accounts: { email: string; displayName?: string; connectedAt?: string }[];\n }> {\n const raw = await this.request<{\n accounts: { accountIdentifier: string; displayName?: string | null; connectedAt?: string }[];\n }>(`/agent/connect/google/accounts/${encodeURIComponent(email)}`, {\n method: \"DELETE\",\n });\n return {\n accounts: raw.accounts.map((a) => ({\n email: a.accountIdentifier,\n displayName: a.displayName ?? undefined,\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n async getGoogleChatCredentials(): Promise<{\n email: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n displayName?: string;\n }> {\n return this.request(\"/agent/google-chat/credentials\");\n }\n\n /**\n * Fetch decrypted credentials for ONE specific connection by its\n * stable connectionId (connection-scoped, vs the provider-scoped\n * `get<Provider>Credentials` helpers). Used by the daemon to resolve\n * a Custom Connection-driven integration's credentials from the\n * exact connection it was installed from — every custom connection\n * shares the `custom` provider id, so provider-scoping is ambiguous.\n *\n * For custom connections `accessToken` is the JSON-encoded secret\n * bundle (the daemon un-bundles it); non-secret fields are on\n * `providerMetadata`. The endpoint enforces that the connection is in\n * the calling agent's effective scope (403 otherwise).\n */\n async getConnectionCredentials(connectionId: string): Promise<{\n provider: string;\n connectionId: string;\n accountIdentifier?: string;\n accessToken?: string;\n providerMetadata?: Record<string, unknown>;\n [key: string]: unknown;\n }> {\n return this.request(\n `/agent/connect/connections/${encodeURIComponent(connectionId)}/credentials`,\n );\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getGithubAccounts()` for the multi-\n * account shape required by Pattern A — explicit selector args on every\n * tool. Retained because the `@alfe.ai/github-mcp` proxy is the\n * only consumer that knows about Pattern A; legacy env-interpolation\n * callers will keep hitting `/credentials` until they move to the proxy.\n */\n async getGithubCredentials(): Promise<{\n login: string;\n accessToken: string;\n }> {\n // GitHub lives on services/connect's universal credential endpoint\n // since the cutover. Connect's `buildCredentialsResponse` returns\n // `{ accessToken, scopes, login }`; we project to the historical\n // shape so callers don't need to know which path served it.\n const raw = await this.request<{\n accessToken: string;\n login: string;\n scopes?: string;\n }>(\"/agent/connect/github/credentials\");\n return { login: raw.login, accessToken: raw.accessToken };\n }\n\n /**\n * Pattern A: multi-account credential fetch for GitHub.\n *\n * Returns every agent-scoped GitHub connection. The caller is expected\n * to require a `login` selector on every credential-touching tool and\n * look up the matching account at dispatch time.\n *\n * GitHub OAuth tokens have no expiry (`tokenLifecycle: \"no_expiry\"`),\n * so there is intentionally no `refreshGithubAccountToken` method — if\n * a token is revoked the user must re-run the OAuth flow.\n *\n * Returned `accounts[i].login` is the GitHub username — the stable\n * cross-session identifier the LLM should pass.\n */\n async getGithubAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n login: string;\n scopes: string;\n }[];\n }> {\n const raw = await this.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n login?: string;\n scopes?: string;\n }[];\n }>(\"/agent/connect/github/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken ?? \"\",\n login: a.login ?? a.accountIdentifier,\n scopes: a.scopes ?? \"\",\n })),\n };\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getXeroAccounts()` for the multi-\n * account shape required by Pattern A — explicit selector args on every\n * tool. This method will be removed once all consumers migrate.\n */\n async getXeroCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n xeroTenantId: string;\n }> {\n // Xero lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n xeroTenantId?: string;\n }>(\"/agent/connect/xero/credentials\");\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n xeroTenantId: raw.xeroTenantId ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Xero. Returns every\n * agent-scoped Xero connection. The caller is expected to require a\n * selector arg (e.g. `xeroTenantId`) on every credential-touching tool\n * and look up the matching account by that selector at dispatch time.\n *\n * Returned `accounts[i].accountIdentifier` is the Xero tenantId — the\n * stable cross-session identifier the LLM should pass.\n */\n async getXeroAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n xeroTenantId: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n xeroTenantId?: string;\n }[];\n }>(\"/agent/connect/xero/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n xeroTenantId: a.xeroTenantId ?? a.accountIdentifier,\n })),\n };\n }\n\n async refreshXeroToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.request(\"/agent/connect/xero/refresh\", { method: \"POST\" });\n }\n\n /**\n * Pattern A: refresh a specific Xero connection by its `accountIdentifier`\n * (the Xero `tenantId`). The legacy `refreshXeroToken()` only refreshes\n * the *primary* connection, which is wrong for multi-tenant Xero where\n * each tenant has its own non-interchangeable access token.\n */\n async refreshXeroAccountToken(xeroTenantId: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/xero/accounts/${encodeURIComponent(xeroTenantId)}/refresh`;\n const raw = await this.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getNotionAccounts()` for the multi-\n * account shape required by Pattern A.\n */\n async getNotionCredentials(): Promise<{\n accessToken: string;\n workspaceId: string;\n workspaceName: string;\n }> {\n // Notion lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n workspaceId?: string;\n workspaceName?: string;\n botId?: string;\n }>(\"/agent/connect/notion/credentials\");\n return {\n accessToken: raw.accessToken,\n workspaceId: raw.workspaceId ?? \"\",\n workspaceName: raw.workspaceName ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Notion. Returns every\n * agent-scoped Notion connection. The caller is expected to require a\n * selector arg (e.g. `workspaceId`) on every credential-touching tool.\n *\n * Returned `accounts[i].accountIdentifier` is the Notion workspaceId.\n */\n async getNotionAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n workspaceId: string;\n workspaceName: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n workspaceId?: string;\n workspaceName?: string;\n botId?: string;\n }[];\n }>(\"/agent/connect/notion/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n workspaceId: a.workspaceId ?? a.accountIdentifier,\n workspaceName: a.workspaceName ?? a.displayName ?? \"\",\n })),\n };\n }\n\n /**\n * @deprecated Returns a single primary Atlassian Connection's credentials\n * (one OAuth user, one cloudId) — the legacy \"pick-the-default-connection\"\n * shape. Atlassian is multi-site by nature (each OAuth user may have\n * access to multiple Cloud sites), so Pattern A plugins MUST use\n * `getAtlassianAccounts()` to discover the full set and dispatch via\n * the `cloudId` selector arg.\n */\n async getAtlassianCredentials(): Promise<{\n accessToken: string;\n refreshToken: string;\n accessTokenExpiresAt: string;\n cloudId: string;\n siteName: string;\n siteUrl: string;\n email: string;\n enabledProducts: string[];\n clientId: string;\n clientSecret: string;\n }> {\n // Atlassian lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n clientId: string;\n clientSecret: string;\n cloudId?: string;\n siteName?: string;\n siteUrl?: string;\n }>(\"/agent/connect/atlassian/credentials\");\n return {\n accessToken: raw.accessToken,\n refreshToken: \"\", // refresh now happens via /agent/connect/atlassian/refresh\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n cloudId: raw.cloudId ?? \"\",\n siteName: raw.siteName ?? \"\",\n siteUrl: raw.siteUrl ?? \"\",\n email: \"\",\n enabledProducts: [],\n clientId: raw.clientId,\n clientSecret: raw.clientSecret,\n };\n }\n\n async refreshAtlassianToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.request(\"/agent/connect/atlassian/refresh\", { method: \"POST\" });\n }\n\n /**\n * Pattern A: multi-account / multi-site credential fetch for Atlassian.\n *\n * Returns every agent-scoped Atlassian Connection. Each Connection is\n * one OAuth user with a single access token and N accessible Cloud\n * sites (`availableSites`). The caller is expected to:\n *\n * 1. Flatten (connection × cloudId) into one MCP child per site.\n * 2. Require a `cloudId` selector on every credential-touching tool.\n * 3. Use the access token bound to the Connection that owns the\n * requested `cloudId` (Atlassian shares one access token across\n * all sites accessible to the OAuth user).\n *\n * Per-account token refresh uses `refreshAtlassianAccountToken(email)`\n * — refreshing one Connection rotates its single access token, which\n * then applies to every cloudId for that Connection.\n *\n * Returned `accounts[i].accountIdentifier` is the OAuth user's email\n * — the stable cross-session identifier for refresh purposes. The LLM\n * never sees this directly: it picks a site via the `cloudId` arg\n * instead.\n */\n async getAtlassianAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n clientId: string;\n clientSecret: string;\n cloudId: string;\n siteName: string;\n siteUrl: string;\n availableSites: {\n id: string;\n url: string;\n name: string;\n scopes?: string[];\n avatarUrl?: string;\n }[];\n }[];\n }> {\n const raw = await this.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n accessTokenExpiresAt?: string;\n clientId?: string;\n clientSecret?: string;\n cloudId?: string;\n siteName?: string;\n siteUrl?: string;\n availableSites?: {\n id: string;\n url: string;\n name: string;\n scopes?: string[];\n avatarUrl?: string;\n }[];\n }[];\n }>(\"/agent/connect/atlassian/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken ?? \"\",\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n clientId: a.clientId ?? \"\",\n clientSecret: a.clientSecret ?? \"\",\n cloudId: a.cloudId ?? \"\",\n siteName: a.siteName ?? \"\",\n siteUrl: a.siteUrl ?? \"\",\n availableSites: a.availableSites ?? [],\n })),\n };\n }\n\n /**\n * Pattern A: refresh a specific Atlassian Connection by `accountIdentifier`\n * (the OAuth user's email).\n *\n * Atlassian rotates refresh tokens (`rotatesRefreshToken: true`); the\n * server-side per-account refresh endpoint handles rotation and\n * persistence. Refreshing one Connection updates its single access\n * token, which applies to every accessible Cloud site (cloudId) for\n * that OAuth user.\n *\n * Returns the new access token + expiry. The proxy is responsible for\n * fanning the new token out to every child server it spawned for\n * cloudIds owned by this Connection.\n */\n async refreshAtlassianAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/atlassian/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getMYOBAccounts()` for the multi-\n * account shape required by Pattern A.\n */\n async getMYOBCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n myobBusinessId: string;\n clientId: string;\n }> {\n // MYOB lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n clientId: string;\n myobBusinessId: string;\n myobBusinessName?: string;\n }>(\"/agent/connect/myob/credentials\");\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n myobBusinessId: raw.myobBusinessId,\n clientId: raw.clientId,\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for MYOB. Returns every\n * agent-scoped MYOB connection. The caller is expected to require a\n * selector arg (e.g. `myobBusinessId` / `accountIdentifier`) on every\n * credential-touching tool.\n *\n * Returned `accounts[i].accountIdentifier` is the MYOB businessId.\n */\n async getMYOBAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n myobBusinessId: string;\n clientId: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n clientId: string;\n myobBusinessId?: string;\n myobBusinessName?: string;\n }[];\n }>(\"/agent/connect/myob/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n myobBusinessId: a.myobBusinessId ?? a.accountIdentifier,\n clientId: a.clientId,\n })),\n };\n }\n\n async refreshMYOBToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.request(\"/agent/connect/myob/refresh\", { method: \"POST\" });\n }\n\n /**\n * @deprecated Returns a single primary credential blob. Use\n * `getSalesforceAccounts()` for the multi-account shape required by\n * Pattern A.\n */\n async getSalesforceCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n instanceUrl: string;\n orgId: string;\n }> {\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n instanceUrl?: string;\n orgId?: string;\n }>(\"/agent/connect/salesforce/credentials\");\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n instanceUrl: raw.instanceUrl ?? \"\",\n orgId: raw.orgId ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Salesforce. Returns every\n * agent-scoped Salesforce connection. One OAuth grant maps to one org, so\n * `accounts[i].accountIdentifier` (and `orgId`) is the Salesforce org id —\n * the selector every credential-touching tool requires.\n */\n async getSalesforceAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n instanceUrl: string;\n orgId: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n instanceUrl?: string;\n orgId?: string;\n }[];\n }>(\"/agent/connect/salesforce/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n instanceUrl: a.instanceUrl ?? \"\",\n orgId: a.orgId ?? a.accountIdentifier,\n })),\n };\n }\n\n /**\n * Refresh the access token for a specific Salesforce org. Salesforce\n * tokens aren't interchangeable across orgs, so the connection is targeted\n * by `accountIdentifier` (the org id) — mirrors `refreshXeroAccountToken`.\n */\n async refreshSalesforceAccountToken(orgId: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/salesforce/accounts/${encodeURIComponent(orgId)}/refresh`;\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * Microsoft 365 (delegated OAuth) credential fetch — single-account shape.\n *\n * @deprecated Use `getMicrosoftAccounts()` and dispatch via the `email`\n * selector once per-account plugins land. Retained because the existing\n * `integrations/connect/microsoft/hooks/post_activate.mjs` writes\n * `mgc` credentials for the single (default) Microsoft account.\n *\n * Returns the agent's effective Microsoft delegated-OAuth credentials.\n * Distinct from `getTeamsCredentials()` (Azure bot credentials for the\n * Teams adapter, which is admin-consent flow on services/microsoft, not\n * delegated OAuth on services/connect).\n */\n async getMicrosoftCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n email?: string;\n microsoftTenantId?: string;\n workspaceDomain?: string;\n }> {\n const raw = await this.request<{\n accessToken?: string;\n accessTokenExpiresAt?: string;\n refreshToken?: string;\n clientId?: string;\n clientSecret?: string;\n accountIdentifier?: string;\n email?: string;\n microsoftTenantId?: string;\n workspaceDomain?: string;\n }>(\"/agent/connect/microsoft/credentials\");\n return {\n accessToken: raw.accessToken ?? \"\",\n accessTokenExpiresAt: raw.accessTokenExpiresAt,\n refreshToken: raw.refreshToken ?? \"\",\n clientId: raw.clientId ?? \"\",\n clientSecret: raw.clientSecret ?? \"\",\n email: raw.email ?? raw.accountIdentifier,\n microsoftTenantId: raw.microsoftTenantId,\n workspaceDomain: raw.workspaceDomain,\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Microsoft 365.\n *\n * Returns every agent-scoped Microsoft connection. The caller is expected\n * to require an `email` selector on every credential-touching tool and\n * look up the matching account at dispatch time.\n *\n * Returned `accounts[i].accountIdentifier` is the user's primary email\n * (or the tid claim as fallback) — the stable cross-session identifier\n * the LLM should pass.\n *\n * Per-account token refresh is exposed via `refreshMicrosoftAccountToken`,\n * NOT `refreshXeroAccountToken` — Microsoft refresh tokens are not\n * interchangeable across (tenant, user) pairs.\n */\n async getMicrosoftAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n email: string;\n microsoftTenantId: string;\n workspaceDomain: string;\n }[];\n }> {\n const raw = await this.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n accessTokenExpiresAt?: string;\n refreshToken?: string;\n clientId?: string;\n clientSecret?: string;\n email?: string;\n microsoftTenantId?: string;\n workspaceDomain?: string;\n }[];\n }>(\"/agent/connect/microsoft/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken ?? \"\",\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n refreshToken: a.refreshToken ?? \"\",\n clientId: a.clientId ?? \"\",\n clientSecret: a.clientSecret ?? \"\",\n email: a.email ?? a.accountIdentifier,\n microsoftTenantId: a.microsoftTenantId ?? \"\",\n workspaceDomain: a.workspaceDomain ?? \"\",\n })),\n };\n }\n\n /**\n * Pattern A: refresh a specific Microsoft 365 connection by its\n * `accountIdentifier`. For Microsoft, `accountIdentifier` is the user's\n * email when the Graph profile fetch succeeded at connect time, and the\n * Azure tenant id (`tid` claim) as fallback. Callers should pass the\n * value returned by `getMicrosoftAccounts()` rather than synthesising\n * an email locally.\n *\n * Microsoft refresh tokens are bound to a specific (tenant, user) pair —\n * they are NOT interchangeable across accounts, so per-account refresh\n * is mandatory. The generic /accounts/{accountIdentifier}/refresh\n * endpoint walks the agent's full visible scope chain to find a matching\n * connection (works for inherited team/project Microsoft connections).\n */\n async refreshMicrosoftAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/microsoft/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n async getTeamsCredentials(): Promise<{\n agentId: string;\n tenantId: string;\n azureAppId: string;\n azureBotId: string;\n azureClientSecret: string;\n botDisplayName?: string;\n teamsTenantId?: string;\n serviceUrl?: string;\n }> {\n return this.request(\"/agent/microsoft/credentials\");\n }\n\n async sendTeamsMessage(data: {\n conversationId: string;\n text?: string;\n adaptiveCard?: Record<string, unknown>;\n }): Promise<{ ok: boolean; activityId: string }> {\n return this.request(\"/agent/microsoft/send\", {\n method: \"POST\",\n body: JSON.stringify(data),\n });\n }\n\n async listTeamsChannels(): Promise<{\n channels: { id: string; name: string; description?: string }[];\n }> {\n return this.request(\"/agent/microsoft/channels\");\n }\n\n async presignAttachments(files: { filename: string; mimeType: string; size: number }[]): Promise<{\n attachments: { id: string; uploadUrl: string; downloadUrl: string; s3Key: string; expiresAt: string }[];\n }> {\n return this.request(\"/agent/chat/attachments/presign\", {\n method: \"POST\",\n body: JSON.stringify({ files }),\n });\n }\n\n async recordActivity(data: {\n userId?: string;\n channel: string;\n role: \"user\" | \"assistant\";\n }): Promise<{ recorded: boolean }> {\n return this.request<{ recorded: boolean }>(\"/agent/activity\", {\n method: \"POST\",\n body: JSON.stringify(data),\n });\n }\n\n // ─── Secrets ──────────────────────────────────────────────────\n //\n // Envelope CRUD + KMS proxy for per-scope encrypted secret storage.\n // Agents never hold a KMS master key — these proxy endpoints mint one-shot\n // AES-256 data keys bound (via KMS encryption context) to\n // `{ tenantId, scope, scopeId, secretId }`. The agent performs AES-256-GCM\n // locally; the backend only ever sees opaque envelopes.\n //\n // Routes resolve to the agent API gateway (mapping key `agent`), where\n // services/secrets registers its routes with pathPrefix `/secrets`. With\n // `apiUrl` set to the host root (e.g. `https://api.alfe.ai`), the full URL\n // is `https://api.alfe.ai/agent/secrets/...`. This is the same mapping as\n // `/agent/integrations/...` etc. — do NOT hit `/secrets/...` on the root\n // host: that's the user-auth dashboard gateway, which rejects agent tokens.\n //\n // `plaintextKey` in responses is base64 — callers MUST decode to a Node\n // `Buffer` immediately and zero it after use. NEVER keep plaintext keys\n // as JS strings (strings are immutable and cannot be wiped).\n\n /**\n * Mint a fresh AES-256 data key for a specific (secret, field) pair. The\n * encryption context is rebuilt server-side from `auth.tenantId` + the body\n * fields including `fieldKey`; the agent cannot forge context for a scope\n * or field it doesn't own. Legacy single-envelope secrets are migrated to\n * `field#value` rows by the data migration, so call with `fieldKey: \"value\"`\n * to reach them.\n */\n async generateSecretDataKey(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n }): Promise<GeneratedDataKey> {\n return this.request<GeneratedDataKey>(\"/agent/secrets/generate-data-key\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Unwrap a wrapped data key so the agent can decrypt the envelope locally.\n * `fieldKey` MUST match the value supplied when the data key was generated\n * (it's bound into KMS encryption context); mismatch fails with\n * `InvalidCiphertextException`.\n */\n async decryptSecretDataKey(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n dataKeyCiphertext: string;\n }): Promise<{ plaintextKey: string }> {\n return this.request<{ plaintextKey: string }>(\"/agent/secrets/decrypt-data-key\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Create a new secret with one or more fields. Encrypted fields must arrive\n * pre-sealed (the agent has already obtained per-field data keys via\n * `generateSecretDataKey({ ..., fieldKey })` and AES-encrypted locally).\n * Plaintext fields ship the value inline.\n */\n async createSecret(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n secretName: string;\n category?: SecretCategory;\n description?: string;\n tags?: string[];\n fields: {\n key: string;\n format?: FieldFormat;\n sensitivity: FieldSensitivity;\n value?: string;\n envelope?: EncryptedEnvelopeV1;\n }[];\n reason?: string;\n }): Promise<SecretAggregate> {\n const { scope, scopeId, secretId, ...body } = args;\n return this.request<SecretAggregate>(\n `/agent/secrets/${encodeURIComponent(scope)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(secretId)}`,\n {\n method: \"PUT\",\n body: JSON.stringify(body),\n },\n );\n }\n\n /** Fetch the secret aggregate plus per-field encrypted envelopes. */\n async getSecret(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n }): Promise<{ aggregate: SecretAggregate; envelopes: FieldEnvelope[] }> {\n return this.request<{ aggregate: SecretAggregate; envelopes: FieldEnvelope[] }>(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}`,\n );\n }\n\n /** Fetch one field. Plaintext: value inline. Encrypted: envelope. */\n async getSecretField(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n }): Promise<{\n key: string;\n sensitivity: FieldSensitivity;\n format?: FieldFormat;\n value?: string;\n envelope?: EncryptedEnvelopeV1;\n rotatedAt?: string;\n createdAt: string;\n updatedAt: string;\n }> {\n return this.request(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}/fields/${encodeURIComponent(args.fieldKey)}`,\n );\n }\n\n /** Add OR rotate one field. */\n async setSecretField(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n sensitivity: FieldSensitivity;\n format?: FieldFormat;\n value?: string;\n envelope?: EncryptedEnvelopeV1;\n reason?: string;\n }): Promise<{ fieldKey: string; rotated: boolean }> {\n const { scope, scopeId, secretId, fieldKey, ...body } = args;\n return this.request(\n `/agent/secrets/${encodeURIComponent(scope)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(secretId)}/fields/${encodeURIComponent(fieldKey)}`,\n { method: \"PUT\", body: JSON.stringify(body) },\n );\n }\n\n /** Remove one field. */\n async removeSecretField(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n }): Promise<void> {\n await this.request(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}/fields/${encodeURIComponent(args.fieldKey)}`,\n { method: \"DELETE\" },\n );\n }\n\n /** Update secret-level metadata (name/description/tags/category). */\n async updateSecretMetadata(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n secretName?: string;\n description?: string;\n tags?: string[];\n category?: SecretCategory;\n reason?: string;\n }): Promise<SecretAggregate> {\n const { scope, scopeId, secretId, ...body } = args;\n return this.request<SecretAggregate>(\n `/agent/secrets/${encodeURIComponent(scope)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(secretId)}`,\n { method: \"PATCH\", body: JSON.stringify(body) },\n );\n }\n\n /** List metadata for secrets in a scope. Optional filters route through the byFacet GSI. */\n async listSecrets(args: {\n scope: SecretScope;\n scopeId: string;\n category?: SecretCategory;\n tag?: string;\n fieldKey?: string;\n }): Promise<SecretMetadata[]> {\n const params = new URLSearchParams();\n if (args.category) params.set(\"category\", args.category);\n if (args.tag) params.set(\"tag\", args.tag);\n if (args.fieldKey) params.set(\"fieldKey\", args.fieldKey);\n const qs = params.toString();\n const resp = await this.request<{ secrets: SecretMetadata[] }>(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}${qs ? `?${qs}` : \"\"}`,\n );\n return resp.secrets;\n }\n\n /** Bounded changelog read — metadata-only audit entries. */\n async getSecretHistory(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n limit?: number;\n cursor?: string;\n }): Promise<{ entries: ChangelogEntry[]; nextCursor?: string }> {\n const params = new URLSearchParams();\n if (args.limit) params.set(\"limit\", String(args.limit));\n if (args.cursor) params.set(\"cursor\", args.cursor);\n const qs = params.toString();\n return this.request(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}/history${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n /** Delete a secret (and all its field rows + tag rows + changelog rows). */\n async deleteSecret(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n }): Promise<void> {\n await this.request<unknown>(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}`,\n { method: \"DELETE\" },\n );\n }\n\n /** Enumerate scopes (org/team/project/agent) this agent can access. */\n async listSecretScopes(): Promise<ScopeInfo[]> {\n const resp = await this.request<{ scopes: ScopeInfo[] }>(\"/agent/secrets/scopes\");\n return resp.scopes;\n }\n\n // ─── Identity ─────────────────────────────────────────────\n //\n // Identity resolution, permission enforcement, and CRM tools.\n // The agent API derives tenantId + agentId from the agent token.\n\n /**\n * Returns the calling agent's own identity context — `{ agentId, tenantId }`\n * decoded server-side from the agent API token. Used by the\n * `@alfe.ai/openclaw-identity` plugin to bootstrap context when the\n * OpenClaw daemon doesn't plumb `ctx.agentId` through to plugin hooks.\n * Plugins should cache this for the daemon's lifetime (single-agent-per-\n * process invariant). One HTTP round-trip per process activate; not for\n * per-call use.\n */\n async whoami(): Promise<{ agentId: string; tenantId: string }> {\n return this.request(\"/agent/identity/whoami\");\n }\n\n async resolveIdentity(args: {\n provider: string;\n platformId: string;\n kind?: \"user\" | \"agent\" | \"service\" | \"bot\" | \"workspace\";\n displayName?: string;\n }): Promise<{\n identityId: string | null;\n status: string;\n created?: boolean;\n reason?: string;\n /**\n * Flattened auriclabs permission strings for the resolved identity\n * (scope-prefixed where applicable). Empty array on miss / org service\n * outage — the runtime gate fails closed in that case.\n */\n permissions: string[];\n }> {\n return this.request(\"/agent/identity/resolve\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async searchIdentities(args?: {\n q?: string;\n status?: string;\n limit?: number;\n }): Promise<{ identities: unknown[] }> {\n const qs = new URLSearchParams();\n if (args?.q) qs.set(\"q\", args.q);\n if (args?.status) qs.set(\"status\", args.status);\n if (args?.limit) qs.set(\"limit\", String(args.limit));\n const query = qs.toString();\n return this.request(`/agent/identity/search${query ? `?${query}` : \"\"}`);\n }\n\n async getIdentityContext(identityId: string): Promise<{\n context: unknown;\n }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/context`);\n }\n\n async mergeIdentities(survivorId: string, args: {\n mergedId: string;\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean; error?: string }> {\n return this.request(`/agent/identity/${encodeURIComponent(survivorId)}/merge`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async unmergeIdentity(identityId: string, args: {\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean; error?: string }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/unmerge`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async addIdentityNote(identityId: string, args: {\n content: string;\n category?: string;\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ noteId: string | null }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/notes`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async tagIdentity(identityId: string, args: {\n tag: string;\n action: \"add\" | \"remove\";\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/tags`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async getIdentityChangelog(identityId: string, args?: {\n limit?: number;\n }): Promise<{ entries: unknown[] }> {\n const qs = new URLSearchParams();\n if (args?.limit) qs.set(\"limit\", String(args.limit));\n const query = qs.toString();\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/changelog${query ? `?${query}` : \"\"}`);\n }\n\n async rollbackIdentity(identityId: string, args: {\n targetVersion: number;\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean; entry?: unknown }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/rollback`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async requestIdentityVerification(args: {\n claimedIdentityId: string;\n requestingIdentityId: string;\n requestingProvider: string;\n requestingPlatformId: string;\n preferredChannel?: \"mobile\" | \"email\";\n /**\n * Phase 2: agent-supplied contact endpoint. When provided, the top-level\n * `preferredChannel` is ignored — the contact's channel wins.\n */\n contact?: { channel: \"email\" | \"mobile\"; value: string };\n }): Promise<{\n verificationId: string;\n channel: string;\n deliveredTo: string;\n expiresAt: string;\n availableChannels: { channel: string; deliveredTo: string }[];\n } | { error: string }> {\n return this.request(\"/agent/identity/verify/request\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async confirmIdentityVerification(args: {\n claimedIdentityId: string;\n verificationId: string;\n phrase: string;\n }): Promise<{\n verified: boolean;\n identityId?: string;\n /** Phase 2: how the confirm resolved — Scenario A vs B. */\n action?: \"merged\" | \"contact_verified\";\n error?: string;\n }> {\n return this.request(\"/agent/identity/verify/confirm\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Update display-shape fields on an Identity. Body excludes `email` /\n * `phone` / `title` / `company` / `metadata` per Section D4 — contacts go\n * via the verify flow, title/company live on OrgMembership, metadata is\n * not agent-writable.\n */\n async updateIdentity(\n identityId: string,\n args: {\n name?: string;\n avatarUrl?: string;\n timezone?: string;\n locale?: string;\n },\n ): Promise<{ ok: boolean }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/update`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Phase 2 (Section H): server-side verification of a Google Chat sender via\n * the agent's existing Google OAuth credentials. Returns the resolved\n * identity (created or matched via Scenario-B email enrichment).\n */\n async resolveGoogleChatSender(args: {\n senderUserId: string;\n spaceId?: string;\n }): Promise<{\n identityId: string | null;\n status: string;\n }> {\n return this.request(\"/agent/google/resolve-sender\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n // ─── Memory ──────────────────────────────────────────────\n //\n // Cloud memory (Turbopuffer vectors + DynamoDB knowledge graph).\n // The agent API derives tenantId + agentId from the agent token.\n\n async memorySearch(query: string, opts?: {\n limit?: number;\n topic?: string;\n subtopic?: string;\n tag?: string;\n includeKnowledge?: boolean;\n }): Promise<{\n facts: { subject: string; predicate: string; object: string; since: string; confidence: number }[];\n memories: { id: string; text: string; topic: string; subtopic: string; tag: string; importance: number; timestamp: number; score: number }[];\n }> {\n return this.request(\"/agent/memory/search\", {\n method: \"POST\",\n body: JSON.stringify({\n query,\n limit: opts?.limit ?? 10,\n topic: opts?.topic,\n subtopic: opts?.subtopic,\n tag: opts?.tag,\n includeKnowledge: opts?.includeKnowledge ?? true,\n }),\n });\n }\n\n async memoryStore(text: string, opts?: {\n topic?: string;\n subtopic?: string;\n tag?: string;\n importance?: number;\n }): Promise<{ memoryId: string }> {\n return this.request(\"/agent/memory/store\", {\n method: \"POST\",\n body: JSON.stringify({\n text,\n topic: opts?.topic ?? \"general\",\n subtopic: opts?.subtopic ?? \"general\",\n tag: opts?.tag ?? \"fact\",\n importance: opts?.importance ?? 0.7,\n }),\n });\n }\n\n async memoryIngest(sessionKey: string, messages: {\n role: string;\n content: string;\n index: number;\n timestamp?: string;\n }[], metadata?: {\n channelId?: string;\n userId?: string;\n userName?: string;\n }): Promise<{ queued: boolean; messageCount: number }> {\n return this.request(\"/agent/memory/ingest\", {\n method: \"POST\",\n body: JSON.stringify({\n sessionKey,\n lastProcessedIndex: messages.length > 0 ? messages[messages.length - 1].index : -1,\n messages,\n metadata,\n }),\n });\n }\n\n async memoryLoadContext(tier?: number, topicHint?: string): Promise<{\n formatted: string;\n [key: string]: unknown;\n }> {\n const params = new URLSearchParams();\n if (tier !== undefined) params.set(\"tier\", String(tier));\n if (topicHint) params.set(\"topicHint\", topicHint);\n const qs = params.toString();\n return this.request(`/agent/memory/context${qs ? `?${qs}` : \"\"}`);\n }\n\n async memoryLookupEntity(subject: string): Promise<{\n subject: string;\n triples: { tripleId: string; predicate: string; object: string; validFrom: string; validTo?: string; confidence: number }[];\n }> {\n return this.request(`/agent/memory/knowledge/entities?subject=${encodeURIComponent(subject)}`);\n }\n\n async memoryNavigate(): Promise<{\n topics: { name: string; tripleCount: number; subtopics: string[] }[];\n cursor: string | null;\n }> {\n return this.request(\"/agent/memory/navigate\");\n }\n\n async memoryDelete(memoryId: string): Promise<{ deleted: boolean }> {\n return this.request(`/agent/memory/${encodeURIComponent(memoryId)}`, {\n method: \"DELETE\",\n });\n }\n\n async memoryStats(): Promise<{\n vectorCount: number;\n tripleCount: number;\n storageEstimateBytes: number;\n lastIngestionAt?: string;\n }> {\n return this.request(\"/agent/memory/stats\");\n }\n\n async memoryLearn(args: {\n text: string;\n source?: string;\n sourceType?: \"file\" | \"url\" | \"inline\";\n metadata?: { sessionId?: string; channelId?: string; userName?: string };\n }): Promise<{\n memoriesStored: number;\n triplesStored: number;\n chunks: number;\n source?: string;\n }> {\n return this.request(\"/agent/memory/learn\", {\n method: \"POST\",\n body: JSON.stringify({\n text: args.text,\n source: args.source,\n sourceType: args.sourceType ?? \"inline\",\n metadata: args.metadata,\n }),\n });\n }\n\n async memoryBootstrapStatus(): Promise<{ synced: boolean; syncedAt?: string }> {\n return this.request(\"/agent/memory/bootstrap-status\");\n }\n\n async memoryBootstrapStatusMark(): Promise<{ synced: true; syncedAt: string }> {\n return this.request(\"/agent/memory/bootstrap-status\", { method: \"POST\" });\n }\n\n // ─── Search ──────────────────────────────────────────────\n\n async searchWeb(params: {\n query: string;\n count?: number;\n offset?: number;\n country?: string;\n freshness?: string;\n }): Promise<unknown> {\n return this.request(\"/agent/search/web\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n async searchImages(params: {\n query: string;\n count?: number;\n }): Promise<unknown> {\n return this.request(\"/agent/search/images\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n async searchNews(params: {\n query: string;\n count?: number;\n freshness?: string;\n }): Promise<unknown> {\n return this.request(\"/agent/search/news\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n // ─── Knowledge resources (org/team/project) ──────────────\n //\n // Scoped knowledge ABOUT a thing being worked on. Search hits\n // `services/knowledge` (RAG); docs/facts/profile hit `services/org`\n // (system of record + per-agent membership gate). All routes resolve\n // to the agent gateway: search under pathPrefix `/knowledge`, org\n // resources under `/org` — same `/agent/...` mapping as the rest.\n //\n // Every write takes an explicit `scopeId`: an agent sees ALL its member\n // scopes (there is no implicit \"current\" one). For the `org` scope,\n // `scopeId` is the tenantId — discover it from `listScopes()`.\n\n /**\n * Semantic search across the agent's member scopes. Fan-out is gated\n * server-side by `listScopes` set-inclusion (fail-closed). Pass\n * `scopeType` + `scopeId` to narrow to one scope; a non-member scope\n * yields empty results (never a cross-scope leak).\n */\n async knowledgeSearch(\n query: string,\n opts?: { limit?: number; scopeType?: KnowledgeScopeType; scopeId?: string },\n ): Promise<KnowledgeSearchResult> {\n return this.request<KnowledgeSearchResult>(\"/agent/knowledge/search\", {\n method: \"POST\",\n body: JSON.stringify({\n query,\n limit: opts?.limit,\n scopeType: opts?.scopeType,\n scopeId: opts?.scopeId,\n }),\n });\n }\n\n /** Enumerate the scopes (org + teams + projects) this agent belongs to. */\n async listScopes(): Promise<{ scopes: KnowledgeScope[] }> {\n return this.request<{ scopes: KnowledgeScope[] }>(\"/agent/org/scopes\");\n }\n\n /** Read a scope's structured knowledge profile (after membership check). */\n async getScopeProfile(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n ): Promise<KnowledgeProfile> {\n return this.request<KnowledgeProfile>(\n `/agent/org/profile/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n );\n }\n\n /** List a scope's facts (non-semantic, full enumeration). */\n async listScopeFacts(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<{ facts: KnowledgeFact[]; nextCursor: string | null }> {\n const qs = new URLSearchParams();\n if (opts?.limit !== undefined) qs.set(\"limit\", String(opts.limit));\n if (opts?.cursor) qs.set(\"cursor\", opts.cursor);\n const query = qs.toString();\n return this.request<{ facts: KnowledgeFact[]; nextCursor: string | null }>(\n `/agent/org/facts/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}${query ? `?${query}` : \"\"}`,\n );\n }\n\n /**\n * Publish a fact at a scope. This is the deliberate cross-agent learning\n * boundary — facts are attributable (the agent is recorded as author) and\n * never an automatic merge. Membership is enforced server-side (403 for a\n * non-member scope).\n */\n async createScopeFact(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n text: string,\n ): Promise<KnowledgeFact> {\n return this.request<KnowledgeFact>(\n `/agent/org/facts/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n { method: \"POST\", body: JSON.stringify({ text }) },\n );\n }\n\n /** Delete a fact at a scope. */\n async deleteScopeFact(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n factId: string,\n ): Promise<{ removed: boolean }> {\n return this.request<{ removed: boolean }>(\n `/agent/org/facts/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(factId)}`,\n { method: \"DELETE\" },\n );\n }\n\n /** List a scope's docs (the org-files corpus; mirrored to shared/<scope>/). */\n async listScopeDocs(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<{ files: KnowledgeDoc[]; nextCursor: string | null }> {\n const qs = new URLSearchParams();\n if (opts?.limit !== undefined) qs.set(\"limit\", String(opts.limit));\n if (opts?.cursor) qs.set(\"cursor\", opts.cursor);\n const query = qs.toString();\n return this.request<{ files: KnowledgeDoc[]; nextCursor: string | null }>(\n `/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}${query ? `?${query}` : \"\"}`,\n );\n }\n\n /**\n * Read the full text of a scope doc. Resolves a presigned download URL\n * from `services/org`, then fetches the bytes directly from S3 (the one\n * legitimate raw fetch in a plugin — same pattern as sync).\n */\n async readScopeDoc(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n filePath: string,\n ): Promise<{ filePath: string; text: string }> {\n const { downloadUrl } = await this.request<{ downloadUrl: string; expiresIn: number }>(\n `/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/download/${encodeFilePath(filePath)}`,\n );\n const res = await fetch(downloadUrl, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });\n if (!res.ok) {\n await res.text();\n throw new Error(`Doc download failed (${String(res.status)})`);\n }\n const text = await res.text();\n return { filePath, text };\n }\n\n /**\n * Write (create or overwrite) a scope doc. Two-step presigned upload:\n * `services/org` returns a signed URL plus `requiredHeaders` (author /\n * authorKind / message as `x-amz-meta-*`) that MUST be sent verbatim on\n * the PUT, alongside the same `Content-Type` that was signed. Author and\n * authorKind are server-set from the agent token — never trusted here.\n */\n async writeScopeDoc(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n filePath: string,\n content: string,\n opts?: { contentType?: string; message?: string },\n ): Promise<{ filePath: string }> {\n const contentType = opts?.contentType ?? \"text/markdown\";\n const presign = await this.request<{\n uploadUrl: string;\n filePath: string;\n expiresIn: number;\n requiredHeaders: Record<string, string>;\n }>(\n `/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/upload/${encodeFilePath(filePath)}`,\n { method: \"POST\", body: JSON.stringify({ contentType, message: opts?.message }) },\n );\n\n const putHeaders = new Headers(presign.requiredHeaders);\n // The presign signed `ContentType` into the URL — the PUT must echo the\n // exact same value or S3 rejects with SignatureDoesNotMatch.\n putHeaders.set(\"Content-Type\", contentType);\n\n const res = await fetch(presign.uploadUrl, {\n method: \"PUT\",\n body: content,\n headers: putHeaders,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n await res.text();\n throw new Error(`Doc upload failed (${String(res.status)})`);\n }\n return { filePath: presign.filePath };\n }\n\n // ─── Database ───────────────────────────────────────────\n\n async registerDatabaseCredentials(): Promise<{\n connectionString: string;\n username: string;\n password: string;\n databases: string[];\n }> {\n return this.request(\"/agent/database/register\", { method: \"POST\" });\n }\n\n async reportDatabaseAudit(entry: {\n database: string;\n collection: string;\n operation: string;\n summary?: string;\n }): Promise<void> {\n await this.request(\"/agent/database/audit\", {\n method: \"POST\",\n body: JSON.stringify(entry),\n }).catch(() => {\n // Fire and forget — audit failure doesn't affect operations\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @alfe.ai/agent-api-client — Agent self-service API client.\n *\n * Used by agents calling /agent/ endpoints. The agent authenticates\n * with its API key — the backend resolves agentId + tenantId from the token.\n * No agent ID needed in paths or config.\n */\n\nexport type {\n IntegrationInstall,\n IntegrationConfigResult,\n RegistryEntry,\n IntegrationConfigSchemaField,\n SecretScope,\n EncryptedEnvelopeV1,\n SecretMetadata,\n SecretAggregate,\n Field,\n FieldFormat,\n FieldSensitivity,\n FieldView,\n FieldEnvelope,\n SecretCategory,\n ChangelogEntry,\n ChangelogAction,\n ChangelogActor,\n ScopeInfo,\n GeneratedDataKey,\n} from \"@alfe/types\";\n\nimport type {\n IntegrationInstall,\n IntegrationConfigResult,\n RegistryEntry,\n SecretScope,\n EncryptedEnvelopeV1,\n SecretMetadata,\n SecretAggregate,\n FieldFormat,\n FieldSensitivity,\n FieldEnvelope,\n SecretCategory,\n ChangelogEntry,\n ScopeInfo,\n GeneratedDataKey,\n} from \"@alfe/types\";\n\nexport interface AgentApiClientConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n// ─── Remote (interactive relay) types ────────────────────\n\nexport interface RemoteSessionInfo {\n sessionId: string;\n agentId: string;\n surface: \"browser\" | \"terminal\";\n status:\n | \"agent_driving\"\n | \"awaiting_human\"\n | \"human_in_control\"\n | \"resuming\"\n | \"completed\"\n | \"expired\"\n | \"failed\";\n url?: string;\n instructions?: string;\n requestedAt?: string;\n}\n\n// ─── Sync types ──────────────────────────────────────────\n\nexport interface SyncAgentInfo {\n agentId: string;\n tenantId: string;\n displayName: string;\n s3Prefix: string;\n status: \"stale\" | \"syncing\" | \"synced\";\n fileCount?: number;\n totalSize?: number;\n lastSync?: string;\n}\n\nexport interface SyncManifestEntry {\n hash: string;\n size: number;\n modified: string;\n etag?: string;\n storageClass?: string;\n compressed?: boolean;\n}\n\nexport interface SyncManifest {\n version: 1;\n agentId: string;\n lastSync: string;\n files: Record<string, SyncManifestEntry>;\n}\n\nexport interface SyncPresignedUrl {\n path: string;\n url: string;\n expiresAt: string;\n}\n\nexport interface SyncConfirmedUpload {\n filePath: string;\n hash: string;\n size: number;\n storageClass: \"STANDARD\" | \"GLACIER_IR\";\n syncedAt: string;\n}\n\nexport interface SyncReconstructFile {\n path: string;\n size: number;\n url: string;\n storageClass?: string;\n compressed?: boolean;\n}\n\nexport interface SyncReconstructBundle {\n agentId: string;\n mode: \"full\" | \"active\" | \"memory\";\n fileCount: number;\n totalSize: number;\n files: SyncReconstructFile[];\n expiresAt: string;\n}\n\nexport interface SyncAgentStats {\n agentId: string;\n standardBytes: number;\n glacierBytes: number;\n fileCount: number;\n lastSyncAt: string | null;\n}\n\nexport interface SyncFileEntry {\n filePath: string;\n size: number;\n modified: string;\n contentHash: string;\n storageClass?: string;\n compressed?: boolean;\n}\n\nexport interface SyncSessionEntry {\n sessionId: string;\n size: number;\n lastModified: string;\n storageClass?: string;\n isArchived: boolean;\n}\n\nexport interface SyncSessionContent {\n sessionId: string;\n content: string;\n compressed: boolean;\n}\n\nexport interface SharedFileEntry {\n filePath: string;\n fileName: string;\n size: number;\n contentType?: string;\n}\n\n// ─── Knowledge resource types (org/team/project) ──────────\n//\n// Scoped, searchable knowledge ABOUT a thing being worked on. The system\n// of record is `services/org` (docs/facts/profile + the membership gate);\n// `services/knowledge` is a pure RAG projection (vector search). `scopeId`\n// for the `org` scope is the tenantId (covers personal + org tenants\n// identically) — agents discover it via `listScopes()`.\n\nexport type KnowledgeScopeType = \"org\" | \"team\" | \"project\";\n\nexport interface KnowledgeScope {\n scopeType: KnowledgeScopeType;\n scopeId: string;\n name: string;\n}\n\nexport interface KnowledgeSearchHit {\n id: string;\n text: string;\n /** Normalized relevance in (0,1]; higher = closer. */\n score: number;\n scopeType: KnowledgeScopeType;\n scopeId: string;\n source: \"doc\" | \"fact\";\n /** Present when `source === \"doc\"` — the canonical file under shared/<scope>/. */\n filePath?: string;\n /** Present when `source === \"fact\"`. */\n factId?: string;\n}\n\nexport interface KnowledgeSearchResult {\n results: KnowledgeSearchHit[];\n /** True when fan-out breadth was capped (more member scopes than the cap). */\n truncatedScopes: boolean;\n}\n\nexport interface KnowledgeProfileLink {\n label: string;\n url: string;\n}\n\nexport interface KnowledgeProfile {\n scopeType: KnowledgeScopeType;\n scopeId: string;\n about: string | null;\n description: string | null;\n links: KnowledgeProfileLink[];\n updatedAt: string | null;\n updatedBy: string | null;\n}\n\nexport interface KnowledgeFact {\n factId: string;\n scopeType: KnowledgeScopeType;\n scopeId: string;\n text: string;\n sourceType: string;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface KnowledgeDoc {\n filePath: string;\n fileName: string;\n contentType?: string;\n size: number;\n uploadedBy?: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Encode each path segment but keep the `/` separators — `encodeURIComponent`\n * would escape the slashes too, breaking greedy proxy routes.\n */\nfunction encodeFilePath(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\n// Per-request budget. Covers cold-start chains (authorizer + handler\n// + downstream OAuth provider) with headroom; the worst legitimate\n// path observed is ~7s (cold Lambda + Atlassian token exchange).\nconst REQUEST_TIMEOUT_MS = 20_000;\n// Statuses worth one retry — API GW synthesizes 500 on authorizer\n// timeout, 502/503/504 cover upstream cold-start and LB transients.\nconst RETRYABLE_STATUS = new Set([500, 502, 503, 504]);\nconst RETRY_DELAY_MS = 500;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction isRetryableNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // AbortSignal.timeout() rejects with TimeoutError (DOMException).\n if (err.name === \"TimeoutError\" || err.name === \"AbortError\") return true;\n // undici wraps socket-level failures (stale keep-alive connections,\n // ECONNRESET on reused pool entries) in a TypeError \"fetch failed\"\n // with the cause attached.\n if (err.name === \"TypeError\") return true;\n return false;\n}\n\n// ─── Self identity types ─────────────────────────────────\n//\n// The agent customizing its OWN identity (name, avatar, voice). agentId +\n// tenantId are resolved from the token, so no id appears in the request.\n\n/** Voice settings — core agent config. Mirrors `VoiceConfig` in `@alfe/types`. */\nexport interface AgentVoiceConfig {\n /** ElevenLabs voice ID; platform default when unset. */\n voiceId?: string;\n ttsModel?: string;\n enabled?: boolean;\n}\n\n/**\n * The agent's own public identity, as returned by `updateSelf`, `generateAvatar`,\n * `presignAvatar`'s finalize (`finalizeAvatar`). This is the public agent\n * projection; only the identity-relevant fields are typed here — the response\n * carries the full public agent record.\n */\nexport interface AgentSelf {\n agentId: string;\n tenantId: string;\n name: string;\n avatarUrl?: string;\n voiceConfig?: AgentVoiceConfig;\n status: string;\n}\n\n/** Result of `presignAvatar` — the agent PUTs bytes to `uploadUrl`, then finalizes with `s3Key`. */\nexport interface AgentAvatarPresign {\n /** Presigned PUT URL to upload the image bytes to. */\n uploadUrl: string;\n /** Object key — echoed back to `finalizeAvatar`. */\n s3Key: string;\n /** Stable public URL the avatar will be served from once finalized. */\n publicUrl: string;\n /** ISO expiry of the presigned PUT URL. */\n expiresAt: string;\n}\n\n/** A voice in the platform catalogue (ElevenLabs), from `listVoices`. */\nexport interface AgentVoice {\n id: string;\n name: string;\n previewUrl: string;\n description: string;\n labels: Record<string, string>;\n category: string;\n}\n\n// ─── Voice one-shot TTS / STT ────────────────────────────\n//\n// These hit the voice service's agent-authed one-shot endpoints\n// (`/voice/tts`, `/voice/stt`), which are Lambda routes co-located on the\n// shared api gateway under the `voice` mapping key. Both are binary flows —\n// TTS returns raw PCM audio bytes, STT accepts raw PCM audio bytes — so they\n// bypass the JSON `{ data: T }` transport used by every other method here and\n// go through `rawFetch` instead. Metering to the tenant credit pool happens\n// server-side; the caller just gets audio (TTS) or a transcript (STT).\n\n/** The ElevenLabs models with a pricing row — the TTS endpoint rejects any other value. */\nexport type VoiceTtsModel = \"eleven_turbo_v2_5\" | \"eleven_multilingual_v2\";\n\nexport interface VoiceTtsArgs {\n /** Text to synthesize (1–5000 chars — the endpoint enforces this). */\n text: string;\n /** ElevenLabs voice id; platform default when unset. */\n voiceId?: string;\n /** TTS model; `eleven_turbo_v2_5` (lower latency) when unset. */\n model?: VoiceTtsModel;\n}\n\n/** Raw synthesized audio plus its PCM framing (from the response headers). */\nexport interface VoiceTtsResult {\n /** Raw little-endian PCM samples — no container. Wrap in WAV to make a playable file. */\n audio: Buffer;\n /** Samples per second (e.g. 24000). */\n sampleRate: number;\n /** Channel count (mono = 1). */\n channels: number;\n /** Bits per sample (e.g. 16). */\n bitDepth: number;\n}\n\nexport interface VoiceSttArgs {\n /** Raw linear16 (16-bit little-endian) mono PCM samples — no WAV/container header. */\n audio: Uint8Array;\n /** Sample rate of `audio` in Hz (8000–48000). */\n sampleRate: number;\n}\n\nexport interface VoiceSttResult {\n text: string;\n /** Deepgram confidence in (0,1]. */\n confidence: number;\n}\n\nexport class AgentApiClient {\n private readonly apiKey: string;\n private readonly apiUrl: string;\n\n constructor(config: AgentApiClientConfig) {\n this.apiKey = config.apiKey;\n this.apiUrl = config.apiUrl;\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n const url = `${this.apiUrl}${path}`;\n const headers = new Headers(options?.headers);\n headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n headers.set(\"Content-Type\", \"application/json\");\n\n // One retry on transient failures. Long-running daemon processes\n // (MCP proxies, the gateway) call this client after multi-hour\n // idle gaps; the first attempt then rides a cold path end-to-end —\n // including the API Gateway Lambda authorizer, which API GW\n // hard-caps at 10s and surfaces as a synthetic 500 WITHOUT ever\n // invoking the route handler. Observed on QA Tester (dev,\n // 2026-06-11): atlassian-mcp-proxy token refreshes failed ~59%\n // of attempts with an 11-12s hang then 500; the same request\n // re-issued seconds later succeeded in ~1.5s. A single retry\n // converts that failure mode into a slow success.\n //\n // Retrying POSTs is safe here: the 500 fires before the handler\n // runs (authorizer timeout), and the one rotating-token endpoint\n // (connect/{provider}/refresh) reads its refresh token from the\n // DB per call, so a duplicate invocation re-rotates cleanly\n // rather than corrupting state.\n let lastError: unknown;\n for (let attempt = 1; attempt <= 2; attempt++) {\n try {\n const res = await fetch(url, {\n ...options,\n headers,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n\n if (!res.ok) {\n await res.text(); // drain body\n const error = new Error(`Agent API request failed (${String(res.status)})`);\n if (attempt === 1 && RETRYABLE_STATUS.has(res.status)) {\n lastError = error;\n await sleep(RETRY_DELAY_MS);\n continue;\n }\n throw error;\n }\n\n const body = (await res.json()) as { data: T };\n return body.data;\n } catch (err) {\n if (attempt === 1 && isRetryableNetworkError(err)) {\n lastError = err;\n await sleep(RETRY_DELAY_MS);\n continue;\n }\n throw err;\n }\n }\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n }\n\n // ─── Sync ────────────────────────────────────────────────\n //\n // Workspace backup. The agent only ever calls /agent/sync/* — file\n // bytes go to/from S3 via presigned URLs (S3 fetch is the one\n // legitimate raw-fetch in a plugin). Dashboard editing uses the\n // user-API at /sync/agents/{agentId}/* and is not exposed here.\n\n async syncRegister(args?: { displayName?: string }): Promise<{ agent: SyncAgentInfo }> {\n return this.request(\"/agent/sync/register\", {\n method: \"POST\",\n body: JSON.stringify(args ?? {}),\n });\n }\n\n async syncGetManifest(): Promise<SyncManifest> {\n return this.request(\"/agent/sync/manifest\");\n }\n\n async syncPresign(args: {\n files: { path: string; operation: \"put\" | \"get\"; contentType?: string }[];\n }): Promise<{ urls: SyncPresignedUrl[] }> {\n return this.request(\"/agent/sync/presign\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncConfirmUpload(args: {\n filePath: string;\n hash: string;\n size: number;\n storageClass?: \"STANDARD\" | \"GLACIER_IR\";\n }): Promise<SyncConfirmedUpload> {\n return this.request(\"/agent/sync/confirm\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncReconstruct(args: {\n mode: \"full\" | \"active\" | \"memory\";\n }): Promise<SyncReconstructBundle> {\n return this.request(\"/agent/sync/reconstruct\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncGetStats(): Promise<SyncAgentStats> {\n return this.request(\"/agent/sync/stats\");\n }\n\n async syncListFiles(args?: { prefix?: string }): Promise<{ files: SyncFileEntry[] }> {\n const qs = new URLSearchParams();\n if (args?.prefix) qs.set(\"prefix\", args.prefix);\n const query = qs.toString();\n return this.request(`/agent/sync/files${query ? `?${query}` : \"\"}`);\n }\n\n async syncListSessions(): Promise<{ sessions: SyncSessionEntry[] }> {\n return this.request(\"/agent/sync/sessions\");\n }\n\n async syncGetSession(sessionId: string): Promise<SyncSessionContent> {\n return this.request(`/agent/sync/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async syncDeleteFile(filePath: string): Promise<{ removed: boolean }> {\n return this.request(`/agent/sync/files/${encodeFilePath(filePath)}`, {\n method: \"DELETE\",\n });\n }\n\n // ─── Shared (org/team/project) files ─────────────────────\n //\n // Used by the sync plugin's shared-sync engine to mirror org-scoped\n // files into the agent's `shared/` directory. Routes live in services/org.\n\n async sharedListFiles(args: {\n scope: \"org\" | \"team\" | \"project\";\n scopeId: string;\n }): Promise<{ files: SharedFileEntry[]; nextCursor: string | null }> {\n return this.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}`,\n );\n }\n\n async sharedDownloadUrl(args: {\n scope: \"org\" | \"team\" | \"project\";\n scopeId: string;\n filePath: string;\n }): Promise<{ downloadUrl: string; expiresIn: number }> {\n return this.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/download/${encodeFilePath(args.filePath)}`,\n );\n }\n\n async listIntegrations(): Promise<IntegrationInstall[]> {\n return this.request<IntegrationInstall[]>(\"/agent/integrations\");\n }\n\n async getIntegrationConfig(integrationId: string): Promise<IntegrationConfigResult> {\n return this.request<IntegrationConfigResult>(\n `/agent/integrations/${encodeURIComponent(integrationId)}/config`,\n );\n }\n\n async updateIntegrationConfig(\n integrationId: string,\n config: Record<string, unknown>,\n ): Promise<void> {\n await this.request<unknown>(\n `/agent/integrations/${encodeURIComponent(integrationId)}`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ config }),\n },\n );\n }\n\n async installIntegration(\n integrationId: string,\n options?: { version?: string; config?: Record<string, unknown> },\n ): Promise<IntegrationInstall> {\n return this.request<IntegrationInstall>(\"/agent/integrations\", {\n method: \"POST\",\n body: JSON.stringify({\n integrationId,\n version: options?.version,\n config: options?.config,\n }),\n });\n }\n\n async removeIntegration(integrationId: string): Promise<IntegrationInstall> {\n return this.request<IntegrationInstall>(\n `/agent/integrations/${encodeURIComponent(integrationId)}`,\n { method: \"DELETE\" },\n );\n }\n\n async getOAuthUrl(\n provider: string,\n scopes?: string[],\n ): Promise<{ url: string; provider: string; expiresIn: number }> {\n const params = new URLSearchParams({ provider });\n if (scopes?.length) params.set(\"scopes\", scopes.join(\",\"));\n return this.request(`/agent/integrations/oauth/url?${params.toString()}`);\n }\n\n async getOAuthStatus(\n provider: string,\n ): Promise<{ provider: string; connected: boolean; config?: Record<string, string> }> {\n return this.request(\n `/agent/integrations/oauth/status?provider=${encodeURIComponent(provider)}`,\n );\n }\n\n async getRegistry(): Promise<{ integrations: RegistryEntry[] }> {\n return this.request<{ integrations: RegistryEntry[] }>(\"/integrations/registry\");\n }\n\n /**\n * Returns every connected Google account for the agent. Multi-account by\n * design — the openclaw-google plugin requires the LLM to pass `email`\n * explicitly to `google_run_command` so an account is always selected\n * deliberately.\n *\n * 2026-05-14 (connections-redesign PR 1): the legacy flat shape (`email`,\n * `refreshToken`, `accessToken`, etc., populated from the default account)\n * is gone. Iterate over `accounts`.\n */\n async getGoogleCredentials(): Promise<{\n accounts: {\n email: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n displayName?: string;\n connectedAt?: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n accountIdentifier: string;\n accessToken?: string;\n refreshToken?: string;\n clientId?: string;\n clientSecret?: string;\n workspaceDomain?: string;\n displayName?: string | null;\n connectedAt?: string;\n }[];\n }>(\"/agent/connect/google/accounts\");\n\n return {\n accounts: raw.accounts.map((a) => ({\n email: a.accountIdentifier,\n refreshToken: a.refreshToken ?? \"\",\n clientId: a.clientId ?? \"\",\n clientSecret: a.clientSecret ?? \"\",\n displayName: a.displayName ?? undefined,\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n async disconnectGoogleAccount(email: string): Promise<{\n accounts: { email: string; displayName?: string; connectedAt?: string }[];\n }> {\n const raw = await this.request<{\n accounts: { accountIdentifier: string; displayName?: string | null; connectedAt?: string }[];\n }>(`/agent/connect/google/accounts/${encodeURIComponent(email)}`, {\n method: \"DELETE\",\n });\n return {\n accounts: raw.accounts.map((a) => ({\n email: a.accountIdentifier,\n displayName: a.displayName ?? undefined,\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n async getGoogleChatCredentials(): Promise<{\n email: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n displayName?: string;\n }> {\n return this.request(\"/agent/google-chat/credentials\");\n }\n\n /**\n * Fetch decrypted credentials for ONE specific connection by its\n * stable connectionId (connection-scoped, vs the provider-scoped\n * `get<Provider>Credentials` helpers). Used by the daemon to resolve\n * a Custom Connection-driven integration's credentials from the\n * exact connection it was installed from — every custom connection\n * shares the `custom` provider id, so provider-scoping is ambiguous.\n *\n * For custom connections `accessToken` is the JSON-encoded secret\n * bundle (the daemon un-bundles it); non-secret fields are on\n * `providerMetadata`. The endpoint enforces that the connection is in\n * the calling agent's effective scope (403 otherwise).\n */\n async getConnectionCredentials(connectionId: string): Promise<{\n provider: string;\n connectionId: string;\n accountIdentifier?: string;\n accessToken?: string;\n providerMetadata?: Record<string, unknown>;\n [key: string]: unknown;\n }> {\n return this.request(\n `/agent/connect/connections/${encodeURIComponent(connectionId)}/credentials`,\n );\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getGithubAccounts()` for the multi-\n * account shape required by Pattern A — explicit selector args on every\n * tool. Retained because the `@alfe.ai/github-mcp` proxy is the\n * only consumer that knows about Pattern A; legacy env-interpolation\n * callers will keep hitting `/credentials` until they move to the proxy.\n */\n async getGithubCredentials(): Promise<{\n login: string;\n accessToken: string;\n }> {\n // GitHub lives on services/connect's universal credential endpoint\n // since the cutover. Connect's `buildCredentialsResponse` returns\n // `{ accessToken, scopes, login }`; we project to the historical\n // shape so callers don't need to know which path served it.\n const raw = await this.request<{\n accessToken: string;\n login: string;\n scopes?: string;\n }>(\"/agent/connect/github/credentials\");\n return { login: raw.login, accessToken: raw.accessToken };\n }\n\n /**\n * Pattern A: multi-account credential fetch for GitHub.\n *\n * Returns every agent-scoped GitHub connection. The caller is expected\n * to require a `login` selector on every credential-touching tool and\n * look up the matching account at dispatch time.\n *\n * GitHub OAuth tokens have no expiry (`tokenLifecycle: \"no_expiry\"`),\n * so there is intentionally no `refreshGithubAccountToken` method — if\n * a token is revoked the user must re-run the OAuth flow.\n *\n * Returned `accounts[i].login` is the GitHub username — the stable\n * cross-session identifier the LLM should pass.\n */\n async getGithubAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n login: string;\n scopes: string;\n }[];\n }> {\n const raw = await this.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n login?: string;\n scopes?: string;\n }[];\n }>(\"/agent/connect/github/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken ?? \"\",\n login: a.login ?? a.accountIdentifier,\n scopes: a.scopes ?? \"\",\n })),\n };\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getXeroAccounts()` for the multi-\n * account shape required by Pattern A — explicit selector args on every\n * tool. This method will be removed once all consumers migrate.\n */\n async getXeroCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n xeroTenantId: string;\n }> {\n // Xero lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n xeroTenantId?: string;\n }>(\"/agent/connect/xero/credentials\");\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n xeroTenantId: raw.xeroTenantId ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Xero. Returns every\n * agent-scoped Xero connection. The caller is expected to require a\n * selector arg (e.g. `xeroTenantId`) on every credential-touching tool\n * and look up the matching account by that selector at dispatch time.\n *\n * Returned `accounts[i].accountIdentifier` is the Xero tenantId — the\n * stable cross-session identifier the LLM should pass.\n */\n async getXeroAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n xeroTenantId: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n xeroTenantId?: string;\n }[];\n }>(\"/agent/connect/xero/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n xeroTenantId: a.xeroTenantId ?? a.accountIdentifier,\n })),\n };\n }\n\n async refreshXeroToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.request(\"/agent/connect/xero/refresh\", { method: \"POST\" });\n }\n\n /**\n * Pattern A: refresh a specific Xero connection by its `accountIdentifier`\n * (the Xero `tenantId`). The legacy `refreshXeroToken()` only refreshes\n * the *primary* connection, which is wrong for multi-tenant Xero where\n * each tenant has its own non-interchangeable access token.\n */\n async refreshXeroAccountToken(xeroTenantId: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/xero/accounts/${encodeURIComponent(xeroTenantId)}/refresh`;\n const raw = await this.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getNotionAccounts()` for the multi-\n * account shape required by Pattern A.\n */\n async getNotionCredentials(): Promise<{\n accessToken: string;\n workspaceId: string;\n workspaceName: string;\n }> {\n // Notion lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n workspaceId?: string;\n workspaceName?: string;\n botId?: string;\n }>(\"/agent/connect/notion/credentials\");\n return {\n accessToken: raw.accessToken,\n workspaceId: raw.workspaceId ?? \"\",\n workspaceName: raw.workspaceName ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Notion. Returns every\n * agent-scoped Notion connection. The caller is expected to require a\n * selector arg (e.g. `workspaceId`) on every credential-touching tool.\n *\n * Returned `accounts[i].accountIdentifier` is the Notion workspaceId.\n */\n async getNotionAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n workspaceId: string;\n workspaceName: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n workspaceId?: string;\n workspaceName?: string;\n botId?: string;\n }[];\n }>(\"/agent/connect/notion/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n workspaceId: a.workspaceId ?? a.accountIdentifier,\n workspaceName: a.workspaceName ?? a.displayName ?? \"\",\n })),\n };\n }\n\n /**\n * @deprecated Returns a single primary Atlassian Connection's credentials\n * (one OAuth user, one cloudId) — the legacy \"pick-the-default-connection\"\n * shape. Atlassian is multi-site by nature (each OAuth user may have\n * access to multiple Cloud sites), so Pattern A plugins MUST use\n * `getAtlassianAccounts()` to discover the full set and dispatch via\n * the `cloudId` selector arg.\n */\n async getAtlassianCredentials(): Promise<{\n accessToken: string;\n refreshToken: string;\n accessTokenExpiresAt: string;\n cloudId: string;\n siteName: string;\n siteUrl: string;\n email: string;\n enabledProducts: string[];\n clientId: string;\n clientSecret: string;\n }> {\n // Atlassian lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n clientId: string;\n clientSecret: string;\n cloudId?: string;\n siteName?: string;\n siteUrl?: string;\n }>(\"/agent/connect/atlassian/credentials\");\n return {\n accessToken: raw.accessToken,\n refreshToken: \"\", // refresh now happens via /agent/connect/atlassian/refresh\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n cloudId: raw.cloudId ?? \"\",\n siteName: raw.siteName ?? \"\",\n siteUrl: raw.siteUrl ?? \"\",\n email: \"\",\n enabledProducts: [],\n clientId: raw.clientId,\n clientSecret: raw.clientSecret,\n };\n }\n\n async refreshAtlassianToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.request(\"/agent/connect/atlassian/refresh\", { method: \"POST\" });\n }\n\n /**\n * Pattern A: multi-account / multi-site credential fetch for Atlassian.\n *\n * Returns every agent-scoped Atlassian Connection. Each Connection is\n * one OAuth user with a single access token and N accessible Cloud\n * sites (`availableSites`). The caller is expected to:\n *\n * 1. Flatten (connection × cloudId) into one MCP child per site.\n * 2. Require a `cloudId` selector on every credential-touching tool.\n * 3. Use the access token bound to the Connection that owns the\n * requested `cloudId` (Atlassian shares one access token across\n * all sites accessible to the OAuth user).\n *\n * Per-account token refresh uses `refreshAtlassianAccountToken(email)`\n * — refreshing one Connection rotates its single access token, which\n * then applies to every cloudId for that Connection.\n *\n * Returned `accounts[i].accountIdentifier` is the OAuth user's email\n * — the stable cross-session identifier for refresh purposes. The LLM\n * never sees this directly: it picks a site via the `cloudId` arg\n * instead.\n */\n async getAtlassianAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n clientId: string;\n clientSecret: string;\n cloudId: string;\n siteName: string;\n siteUrl: string;\n availableSites: {\n id: string;\n url: string;\n name: string;\n scopes?: string[];\n avatarUrl?: string;\n }[];\n }[];\n }> {\n const raw = await this.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n accessTokenExpiresAt?: string;\n clientId?: string;\n clientSecret?: string;\n cloudId?: string;\n siteName?: string;\n siteUrl?: string;\n availableSites?: {\n id: string;\n url: string;\n name: string;\n scopes?: string[];\n avatarUrl?: string;\n }[];\n }[];\n }>(\"/agent/connect/atlassian/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken ?? \"\",\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n clientId: a.clientId ?? \"\",\n clientSecret: a.clientSecret ?? \"\",\n cloudId: a.cloudId ?? \"\",\n siteName: a.siteName ?? \"\",\n siteUrl: a.siteUrl ?? \"\",\n availableSites: a.availableSites ?? [],\n })),\n };\n }\n\n /**\n * Pattern A: refresh a specific Atlassian Connection by `accountIdentifier`\n * (the OAuth user's email).\n *\n * Atlassian rotates refresh tokens (`rotatesRefreshToken: true`); the\n * server-side per-account refresh endpoint handles rotation and\n * persistence. Refreshing one Connection updates its single access\n * token, which applies to every accessible Cloud site (cloudId) for\n * that OAuth user.\n *\n * Returns the new access token + expiry. The proxy is responsible for\n * fanning the new token out to every child server it spawned for\n * cloudIds owned by this Connection.\n */\n async refreshAtlassianAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/atlassian/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * @deprecated Returns a single primary credential blob (legacy \"pick-the-\n * default-connection\" shape). Use `getMYOBAccounts()` for the multi-\n * account shape required by Pattern A.\n */\n async getMYOBCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n myobBusinessId: string;\n clientId: string;\n }> {\n // MYOB lives on services/connect since the cutover.\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n clientId: string;\n myobBusinessId: string;\n myobBusinessName?: string;\n }>(\"/agent/connect/myob/credentials\");\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n myobBusinessId: raw.myobBusinessId,\n clientId: raw.clientId,\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for MYOB. Returns every\n * agent-scoped MYOB connection. The caller is expected to require a\n * selector arg (e.g. `myobBusinessId` / `accountIdentifier`) on every\n * credential-touching tool.\n *\n * Returned `accounts[i].accountIdentifier` is the MYOB businessId.\n */\n async getMYOBAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n myobBusinessId: string;\n clientId: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n clientId: string;\n myobBusinessId?: string;\n myobBusinessName?: string;\n }[];\n }>(\"/agent/connect/myob/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n myobBusinessId: a.myobBusinessId ?? a.accountIdentifier,\n clientId: a.clientId,\n })),\n };\n }\n\n async refreshMYOBToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.request(\"/agent/connect/myob/refresh\", { method: \"POST\" });\n }\n\n /**\n * @deprecated Returns a single primary credential blob. Use\n * `getSalesforceAccounts()` for the multi-account shape required by\n * Pattern A.\n */\n async getSalesforceCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n instanceUrl: string;\n orgId: string;\n }> {\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n instanceUrl?: string;\n orgId?: string;\n }>(\"/agent/connect/salesforce/credentials\");\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n instanceUrl: raw.instanceUrl ?? \"\",\n orgId: raw.orgId ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Salesforce. Returns every\n * agent-scoped Salesforce connection. One OAuth grant maps to one org, so\n * `accounts[i].accountIdentifier` (and `orgId`) is the Salesforce org id —\n * the selector every credential-touching tool requires.\n */\n async getSalesforceAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n instanceUrl: string;\n orgId: string;\n }[];\n }> {\n const raw = await this.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n instanceUrl?: string;\n orgId?: string;\n }[];\n }>(\"/agent/connect/salesforce/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken,\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n instanceUrl: a.instanceUrl ?? \"\",\n orgId: a.orgId ?? a.accountIdentifier,\n })),\n };\n }\n\n /**\n * Refresh the access token for a specific Salesforce org. Salesforce\n * tokens aren't interchangeable across orgs, so the connection is targeted\n * by `accountIdentifier` (the org id) — mirrors `refreshXeroAccountToken`.\n */\n async refreshSalesforceAccountToken(orgId: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/salesforce/accounts/${encodeURIComponent(orgId)}/refresh`;\n const raw = await this.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * Microsoft 365 (delegated OAuth) credential fetch — single-account shape.\n *\n * @deprecated Use `getMicrosoftAccounts()` and dispatch via the `email`\n * selector once per-account plugins land. Retained because the existing\n * `integrations/connect/microsoft/hooks/post_activate.mjs` writes\n * `mgc` credentials for the single (default) Microsoft account.\n *\n * Returns the agent's effective Microsoft delegated-OAuth credentials.\n * Distinct from `getTeamsCredentials()` (Azure bot credentials for the\n * Teams adapter, which is admin-consent flow on services/microsoft, not\n * delegated OAuth on services/connect).\n */\n async getMicrosoftCredentials(): Promise<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n email?: string;\n microsoftTenantId?: string;\n workspaceDomain?: string;\n }> {\n const raw = await this.request<{\n accessToken?: string;\n accessTokenExpiresAt?: string;\n refreshToken?: string;\n clientId?: string;\n clientSecret?: string;\n accountIdentifier?: string;\n email?: string;\n microsoftTenantId?: string;\n workspaceDomain?: string;\n }>(\"/agent/connect/microsoft/credentials\");\n return {\n accessToken: raw.accessToken ?? \"\",\n accessTokenExpiresAt: raw.accessTokenExpiresAt,\n refreshToken: raw.refreshToken ?? \"\",\n clientId: raw.clientId ?? \"\",\n clientSecret: raw.clientSecret ?? \"\",\n email: raw.email ?? raw.accountIdentifier,\n microsoftTenantId: raw.microsoftTenantId,\n workspaceDomain: raw.workspaceDomain,\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Microsoft 365.\n *\n * Returns every agent-scoped Microsoft connection. The caller is expected\n * to require an `email` selector on every credential-touching tool and\n * look up the matching account at dispatch time.\n *\n * Returned `accounts[i].accountIdentifier` is the user's primary email\n * (or the tid claim as fallback) — the stable cross-session identifier\n * the LLM should pass.\n *\n * Per-account token refresh is exposed via `refreshMicrosoftAccountToken`,\n * NOT `refreshXeroAccountToken` — Microsoft refresh tokens are not\n * interchangeable across (tenant, user) pairs.\n */\n async getMicrosoftAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n email: string;\n microsoftTenantId: string;\n workspaceDomain: string;\n }[];\n }> {\n const raw = await this.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n accessTokenExpiresAt?: string;\n refreshToken?: string;\n clientId?: string;\n clientSecret?: string;\n email?: string;\n microsoftTenantId?: string;\n workspaceDomain?: string;\n }[];\n }>(\"/agent/connect/microsoft/accounts\");\n return {\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n accessToken: a.accessToken ?? \"\",\n accessTokenExpiresAt: a.accessTokenExpiresAt ?? \"\",\n refreshToken: a.refreshToken ?? \"\",\n clientId: a.clientId ?? \"\",\n clientSecret: a.clientSecret ?? \"\",\n email: a.email ?? a.accountIdentifier,\n microsoftTenantId: a.microsoftTenantId ?? \"\",\n workspaceDomain: a.workspaceDomain ?? \"\",\n })),\n };\n }\n\n /**\n * Pattern A: refresh a specific Microsoft 365 connection by its\n * `accountIdentifier`. For Microsoft, `accountIdentifier` is the user's\n * email when the Graph profile fetch succeeded at connect time, and the\n * Azure tenant id (`tid` claim) as fallback. Callers should pass the\n * value returned by `getMicrosoftAccounts()` rather than synthesising\n * an email locally.\n *\n * Microsoft refresh tokens are bound to a specific (tenant, user) pair —\n * they are NOT interchangeable across accounts, so per-account refresh\n * is mandatory. The generic /accounts/{accountIdentifier}/refresh\n * endpoint walks the agent's full visible scope chain to find a matching\n * connection (works for inherited team/project Microsoft connections).\n */\n async refreshMicrosoftAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/microsoft/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n async getTeamsCredentials(): Promise<{\n agentId: string;\n tenantId: string;\n azureAppId: string;\n azureBotId: string;\n azureClientSecret: string;\n botDisplayName?: string;\n teamsTenantId?: string;\n serviceUrl?: string;\n }> {\n return this.request(\"/agent/microsoft/credentials\");\n }\n\n async sendTeamsMessage(data: {\n conversationId: string;\n text?: string;\n adaptiveCard?: Record<string, unknown>;\n }): Promise<{ ok: boolean; activityId: string }> {\n return this.request(\"/agent/microsoft/send\", {\n method: \"POST\",\n body: JSON.stringify(data),\n });\n }\n\n async listTeamsChannels(): Promise<{\n channels: { id: string; name: string; description?: string }[];\n }> {\n return this.request(\"/agent/microsoft/channels\");\n }\n\n async presignAttachments(files: { filename: string; mimeType: string; size: number }[]): Promise<{\n attachments: { id: string; uploadUrl: string; downloadUrl: string; s3Key: string; expiresAt: string }[];\n }> {\n return this.request(\"/agent/chat/attachments/presign\", {\n method: \"POST\",\n body: JSON.stringify({ files }),\n });\n }\n\n async recordActivity(data: {\n userId?: string;\n channel: string;\n role: \"user\" | \"assistant\";\n }): Promise<{ recorded: boolean }> {\n return this.request<{ recorded: boolean }>(\"/agent/activity\", {\n method: \"POST\",\n body: JSON.stringify(data),\n });\n }\n\n // ─── Self identity ────────────────────────────────────────────\n //\n // The agent customizes its OWN name / voice / avatar. agentId + tenantId are\n // resolved from the API key — never passed in the path or body. `avatarUrl`\n // is server-set only, via generate/upload, so `updateSelf` intentionally\n // does not accept it.\n\n /** Update the agent's own name and/or voice config. Returns the updated agent. */\n async updateSelf(update: { name?: string; voiceConfig?: AgentVoiceConfig }): Promise<AgentSelf> {\n return this.request<AgentSelf>(\"/agent/self\", {\n method: \"PATCH\",\n body: JSON.stringify(update),\n });\n }\n\n /**\n * Generate the agent's own avatar from a text prompt. The image is generated,\n * stored, and set on the agent server-side; returns the updated agent.\n */\n async generateAvatar(args: { prompt: string }): Promise<AgentSelf> {\n return this.request<AgentSelf>(\"/agent/avatar/generate\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Get a presigned PUT URL to upload a new avatar image. Upload the bytes to\n * `uploadUrl`, then call `finalizeAvatar(s3Key)` to set it on the agent.\n */\n async presignAvatar(args: { mimeType: string; size: number }): Promise<AgentAvatarPresign> {\n return this.request<AgentAvatarPresign>(\"/agent/avatar/presign\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Finalize an avatar upload — validates ownership + size, then sets the\n * agent's `avatarUrl` server-side. Returns the updated agent.\n */\n async finalizeAvatar(s3Key: string): Promise<AgentSelf> {\n return this.request<AgentSelf>(\"/agent/avatar\", {\n method: \"POST\",\n body: JSON.stringify({ s3Key }),\n });\n }\n\n /** List the platform voice catalogue (ElevenLabs) so the agent can pick its own voice. */\n async listVoices(): Promise<{ voices: AgentVoice[] }> {\n return this.request<{ voices: AgentVoice[] }>(\"/agent/voices\");\n }\n\n // ─── Secrets ──────────────────────────────────────────────────\n //\n // Envelope CRUD + KMS proxy for per-scope encrypted secret storage.\n // Agents never hold a KMS master key — these proxy endpoints mint one-shot\n // AES-256 data keys bound (via KMS encryption context) to\n // `{ tenantId, scope, scopeId, secretId }`. The agent performs AES-256-GCM\n // locally; the backend only ever sees opaque envelopes.\n //\n // Routes resolve to the agent API gateway (mapping key `agent`), where\n // services/secrets registers its routes with pathPrefix `/secrets`. With\n // `apiUrl` set to the host root (e.g. `https://api.alfe.ai`), the full URL\n // is `https://api.alfe.ai/agent/secrets/...`. This is the same mapping as\n // `/agent/integrations/...` etc. — do NOT hit `/secrets/...` on the root\n // host: that's the user-auth dashboard gateway, which rejects agent tokens.\n //\n // `plaintextKey` in responses is base64 — callers MUST decode to a Node\n // `Buffer` immediately and zero it after use. NEVER keep plaintext keys\n // as JS strings (strings are immutable and cannot be wiped).\n\n /**\n * Mint a fresh AES-256 data key for a specific (secret, field) pair. The\n * encryption context is rebuilt server-side from `auth.tenantId` + the body\n * fields including `fieldKey`; the agent cannot forge context for a scope\n * or field it doesn't own. Legacy single-envelope secrets are migrated to\n * `field#value` rows by the data migration, so call with `fieldKey: \"value\"`\n * to reach them.\n */\n async generateSecretDataKey(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n }): Promise<GeneratedDataKey> {\n return this.request<GeneratedDataKey>(\"/agent/secrets/generate-data-key\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Unwrap a wrapped data key so the agent can decrypt the envelope locally.\n * `fieldKey` MUST match the value supplied when the data key was generated\n * (it's bound into KMS encryption context); mismatch fails with\n * `InvalidCiphertextException`.\n */\n async decryptSecretDataKey(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n dataKeyCiphertext: string;\n }): Promise<{ plaintextKey: string }> {\n return this.request<{ plaintextKey: string }>(\"/agent/secrets/decrypt-data-key\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Create a new secret with one or more fields. Encrypted fields must arrive\n * pre-sealed (the agent has already obtained per-field data keys via\n * `generateSecretDataKey({ ..., fieldKey })` and AES-encrypted locally).\n * Plaintext fields ship the value inline.\n */\n async createSecret(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n secretName: string;\n category?: SecretCategory;\n description?: string;\n tags?: string[];\n fields: {\n key: string;\n format?: FieldFormat;\n sensitivity: FieldSensitivity;\n value?: string;\n envelope?: EncryptedEnvelopeV1;\n }[];\n reason?: string;\n }): Promise<SecretAggregate> {\n const { scope, scopeId, secretId, ...body } = args;\n return this.request<SecretAggregate>(\n `/agent/secrets/${encodeURIComponent(scope)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(secretId)}`,\n {\n method: \"PUT\",\n body: JSON.stringify(body),\n },\n );\n }\n\n /** Fetch the secret aggregate plus per-field encrypted envelopes. */\n async getSecret(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n }): Promise<{ aggregate: SecretAggregate; envelopes: FieldEnvelope[] }> {\n return this.request<{ aggregate: SecretAggregate; envelopes: FieldEnvelope[] }>(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}`,\n );\n }\n\n /** Fetch one field. Plaintext: value inline. Encrypted: envelope. */\n async getSecretField(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n }): Promise<{\n key: string;\n sensitivity: FieldSensitivity;\n format?: FieldFormat;\n value?: string;\n envelope?: EncryptedEnvelopeV1;\n rotatedAt?: string;\n createdAt: string;\n updatedAt: string;\n }> {\n return this.request(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}/fields/${encodeURIComponent(args.fieldKey)}`,\n );\n }\n\n /** Add OR rotate one field. */\n async setSecretField(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n sensitivity: FieldSensitivity;\n format?: FieldFormat;\n value?: string;\n envelope?: EncryptedEnvelopeV1;\n reason?: string;\n }): Promise<{ fieldKey: string; rotated: boolean }> {\n const { scope, scopeId, secretId, fieldKey, ...body } = args;\n return this.request(\n `/agent/secrets/${encodeURIComponent(scope)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(secretId)}/fields/${encodeURIComponent(fieldKey)}`,\n { method: \"PUT\", body: JSON.stringify(body) },\n );\n }\n\n /** Remove one field. */\n async removeSecretField(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n fieldKey: string;\n }): Promise<void> {\n await this.request(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}/fields/${encodeURIComponent(args.fieldKey)}`,\n { method: \"DELETE\" },\n );\n }\n\n /** Update secret-level metadata (name/description/tags/category). */\n async updateSecretMetadata(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n secretName?: string;\n description?: string;\n tags?: string[];\n category?: SecretCategory;\n reason?: string;\n }): Promise<SecretAggregate> {\n const { scope, scopeId, secretId, ...body } = args;\n return this.request<SecretAggregate>(\n `/agent/secrets/${encodeURIComponent(scope)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(secretId)}`,\n { method: \"PATCH\", body: JSON.stringify(body) },\n );\n }\n\n /** List metadata for secrets in a scope. Optional filters route through the byFacet GSI. */\n async listSecrets(args: {\n scope: SecretScope;\n scopeId: string;\n category?: SecretCategory;\n tag?: string;\n fieldKey?: string;\n }): Promise<SecretMetadata[]> {\n const params = new URLSearchParams();\n if (args.category) params.set(\"category\", args.category);\n if (args.tag) params.set(\"tag\", args.tag);\n if (args.fieldKey) params.set(\"fieldKey\", args.fieldKey);\n const qs = params.toString();\n const resp = await this.request<{ secrets: SecretMetadata[] }>(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}${qs ? `?${qs}` : \"\"}`,\n );\n return resp.secrets;\n }\n\n /** Bounded changelog read — metadata-only audit entries. */\n async getSecretHistory(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n limit?: number;\n cursor?: string;\n }): Promise<{ entries: ChangelogEntry[]; nextCursor?: string }> {\n const params = new URLSearchParams();\n if (args.limit) params.set(\"limit\", String(args.limit));\n if (args.cursor) params.set(\"cursor\", args.cursor);\n const qs = params.toString();\n return this.request(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}/history${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n /** Delete a secret (and all its field rows + tag rows + changelog rows). */\n async deleteSecret(args: {\n scope: SecretScope;\n scopeId: string;\n secretId: string;\n }): Promise<void> {\n await this.request<unknown>(\n `/agent/secrets/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/${encodeURIComponent(args.secretId)}`,\n { method: \"DELETE\" },\n );\n }\n\n /** Enumerate scopes (org/team/project/agent) this agent can access. */\n async listSecretScopes(): Promise<ScopeInfo[]> {\n const resp = await this.request<{ scopes: ScopeInfo[] }>(\"/agent/secrets/scopes\");\n return resp.scopes;\n }\n\n // ─── Identity ─────────────────────────────────────────────\n //\n // Identity resolution, permission enforcement, and CRM tools.\n // The agent API derives tenantId + agentId from the agent token.\n\n /**\n * Returns the calling agent's own identity context — `{ agentId, tenantId }`\n * decoded server-side from the agent API token. Used by the\n * `@alfe.ai/openclaw-identity` plugin to bootstrap context when the\n * OpenClaw daemon doesn't plumb `ctx.agentId` through to plugin hooks.\n * Plugins should cache this for the daemon's lifetime (single-agent-per-\n * process invariant). One HTTP round-trip per process activate; not for\n * per-call use.\n */\n async whoami(): Promise<{ agentId: string; tenantId: string }> {\n return this.request(\"/agent/identity/whoami\");\n }\n\n async resolveIdentity(args: {\n provider: string;\n platformId: string;\n kind?: \"user\" | \"agent\" | \"service\" | \"bot\" | \"workspace\";\n displayName?: string;\n }): Promise<{\n identityId: string | null;\n status: string;\n created?: boolean;\n reason?: string;\n /**\n * Flattened auriclabs permission strings for the resolved identity\n * (scope-prefixed where applicable). Empty array on miss / org service\n * outage — the runtime gate fails closed in that case.\n */\n permissions: string[];\n }> {\n return this.request(\"/agent/identity/resolve\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async searchIdentities(args?: {\n q?: string;\n status?: string;\n limit?: number;\n }): Promise<{ identities: unknown[] }> {\n const qs = new URLSearchParams();\n if (args?.q) qs.set(\"q\", args.q);\n if (args?.status) qs.set(\"status\", args.status);\n if (args?.limit) qs.set(\"limit\", String(args.limit));\n const query = qs.toString();\n return this.request(`/agent/identity/search${query ? `?${query}` : \"\"}`);\n }\n\n async getIdentityContext(identityId: string): Promise<{\n context: unknown;\n }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/context`);\n }\n\n async mergeIdentities(survivorId: string, args: {\n mergedId: string;\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean; error?: string }> {\n return this.request(`/agent/identity/${encodeURIComponent(survivorId)}/merge`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async unmergeIdentity(identityId: string, args: {\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean; error?: string }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/unmerge`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async addIdentityNote(identityId: string, args: {\n content: string;\n category?: string;\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ noteId: string | null }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/notes`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async tagIdentity(identityId: string, args: {\n tag: string;\n action: \"add\" | \"remove\";\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/tags`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async getIdentityChangelog(identityId: string, args?: {\n limit?: number;\n }): Promise<{ entries: unknown[] }> {\n const qs = new URLSearchParams();\n if (args?.limit) qs.set(\"limit\", String(args.limit));\n const query = qs.toString();\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/changelog${query ? `?${query}` : \"\"}`);\n }\n\n async rollbackIdentity(identityId: string, args: {\n targetVersion: number;\n changedBy: { type: string; id: string; name?: string };\n }): Promise<{ ok: boolean; entry?: unknown }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/rollback`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async requestIdentityVerification(args: {\n claimedIdentityId: string;\n requestingIdentityId: string;\n requestingProvider: string;\n requestingPlatformId: string;\n preferredChannel?: \"mobile\" | \"email\";\n /**\n * Phase 2: agent-supplied contact endpoint. When provided, the top-level\n * `preferredChannel` is ignored — the contact's channel wins.\n */\n contact?: { channel: \"email\" | \"mobile\"; value: string };\n }): Promise<{\n verificationId: string;\n channel: string;\n deliveredTo: string;\n expiresAt: string;\n availableChannels: { channel: string; deliveredTo: string }[];\n } | { error: string }> {\n return this.request(\"/agent/identity/verify/request\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async confirmIdentityVerification(args: {\n claimedIdentityId: string;\n verificationId: string;\n phrase: string;\n }): Promise<{\n verified: boolean;\n identityId?: string;\n /** Phase 2: how the confirm resolved — Scenario A vs B. */\n action?: \"merged\" | \"contact_verified\";\n error?: string;\n }> {\n return this.request(\"/agent/identity/verify/confirm\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Update display-shape fields on an Identity. Body excludes `email` /\n * `phone` / `title` / `company` / `metadata` per Section D4 — contacts go\n * via the verify flow, title/company live on OrgMembership, metadata is\n * not agent-writable.\n */\n async updateIdentity(\n identityId: string,\n args: {\n name?: string;\n avatarUrl?: string;\n timezone?: string;\n locale?: string;\n },\n ): Promise<{ ok: boolean }> {\n return this.request(`/agent/identity/${encodeURIComponent(identityId)}/update`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n /**\n * Phase 2 (Section H): server-side verification of a Google Chat sender via\n * the agent's existing Google OAuth credentials. Returns the resolved\n * identity (created or matched via Scenario-B email enrichment).\n */\n async resolveGoogleChatSender(args: {\n senderUserId: string;\n spaceId?: string;\n }): Promise<{\n identityId: string | null;\n status: string;\n }> {\n return this.request(\"/agent/google/resolve-sender\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n // ─── Memory ──────────────────────────────────────────────\n //\n // Cloud memory (Turbopuffer vectors + DynamoDB knowledge graph).\n // The agent API derives tenantId + agentId from the agent token.\n\n async memorySearch(query: string, opts?: {\n limit?: number;\n topic?: string;\n subtopic?: string;\n tag?: string;\n includeKnowledge?: boolean;\n }): Promise<{\n facts: { subject: string; predicate: string; object: string; since: string; confidence: number }[];\n memories: { id: string; text: string; topic: string; subtopic: string; tag: string; importance: number; timestamp: number; score: number }[];\n }> {\n return this.request(\"/agent/memory/search\", {\n method: \"POST\",\n body: JSON.stringify({\n query,\n limit: opts?.limit ?? 10,\n topic: opts?.topic,\n subtopic: opts?.subtopic,\n tag: opts?.tag,\n includeKnowledge: opts?.includeKnowledge ?? true,\n }),\n });\n }\n\n async memoryStore(text: string, opts?: {\n topic?: string;\n subtopic?: string;\n tag?: string;\n importance?: number;\n }): Promise<{ memoryId: string }> {\n return this.request(\"/agent/memory/store\", {\n method: \"POST\",\n body: JSON.stringify({\n text,\n topic: opts?.topic ?? \"general\",\n subtopic: opts?.subtopic ?? \"general\",\n tag: opts?.tag ?? \"fact\",\n importance: opts?.importance ?? 0.7,\n }),\n });\n }\n\n async memoryIngest(sessionKey: string, messages: {\n role: string;\n content: string;\n index: number;\n timestamp?: string;\n }[], metadata?: {\n channelId?: string;\n userId?: string;\n userName?: string;\n }): Promise<{ queued: boolean; messageCount: number }> {\n return this.request(\"/agent/memory/ingest\", {\n method: \"POST\",\n body: JSON.stringify({\n sessionKey,\n lastProcessedIndex: messages.length > 0 ? messages[messages.length - 1].index : -1,\n messages,\n metadata,\n }),\n });\n }\n\n async memoryLoadContext(tier?: number, topicHint?: string): Promise<{\n formatted: string;\n [key: string]: unknown;\n }> {\n const params = new URLSearchParams();\n if (tier !== undefined) params.set(\"tier\", String(tier));\n if (topicHint) params.set(\"topicHint\", topicHint);\n const qs = params.toString();\n return this.request(`/agent/memory/context${qs ? `?${qs}` : \"\"}`);\n }\n\n async memoryLookupEntity(subject: string): Promise<{\n subject: string;\n triples: { tripleId: string; predicate: string; object: string; validFrom: string; validTo?: string; confidence: number }[];\n }> {\n return this.request(`/agent/memory/knowledge/entities?subject=${encodeURIComponent(subject)}`);\n }\n\n async memoryNavigate(): Promise<{\n topics: { name: string; tripleCount: number; subtopics: string[] }[];\n cursor: string | null;\n }> {\n return this.request(\"/agent/memory/navigate\");\n }\n\n async memoryDelete(memoryId: string): Promise<{ deleted: boolean }> {\n return this.request(`/agent/memory/${encodeURIComponent(memoryId)}`, {\n method: \"DELETE\",\n });\n }\n\n async memoryStats(): Promise<{\n vectorCount: number;\n tripleCount: number;\n storageEstimateBytes: number;\n lastIngestionAt?: string;\n }> {\n return this.request(\"/agent/memory/stats\");\n }\n\n async memoryLearn(args: {\n text: string;\n source?: string;\n sourceType?: \"file\" | \"url\" | \"inline\";\n metadata?: { sessionId?: string; channelId?: string; userName?: string };\n }): Promise<{\n memoriesStored: number;\n triplesStored: number;\n chunks: number;\n source?: string;\n }> {\n return this.request(\"/agent/memory/learn\", {\n method: \"POST\",\n body: JSON.stringify({\n text: args.text,\n source: args.source,\n sourceType: args.sourceType ?? \"inline\",\n metadata: args.metadata,\n }),\n });\n }\n\n async memoryBootstrapStatus(): Promise<{ synced: boolean; syncedAt?: string }> {\n return this.request(\"/agent/memory/bootstrap-status\");\n }\n\n async memoryBootstrapStatusMark(): Promise<{ synced: true; syncedAt: string }> {\n return this.request(\"/agent/memory/bootstrap-status\", { method: \"POST\" });\n }\n\n // ─── Search ──────────────────────────────────────────────\n\n async searchWeb(params: {\n query: string;\n count?: number;\n offset?: number;\n country?: string;\n freshness?: string;\n }): Promise<unknown> {\n return this.request(\"/agent/search/web\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n async searchImages(params: {\n query: string;\n count?: number;\n }): Promise<unknown> {\n return this.request(\"/agent/search/images\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n async searchNews(params: {\n query: string;\n count?: number;\n freshness?: string;\n }): Promise<unknown> {\n return this.request(\"/agent/search/news\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n // ─── Knowledge resources (org/team/project) ──────────────\n //\n // Scoped knowledge ABOUT a thing being worked on. Search hits\n // `services/knowledge` (RAG); docs/facts/profile hit `services/org`\n // (system of record + per-agent membership gate). All routes resolve\n // to the agent gateway: search under pathPrefix `/knowledge`, org\n // resources under `/org` — same `/agent/...` mapping as the rest.\n //\n // Every write takes an explicit `scopeId`: an agent sees ALL its member\n // scopes (there is no implicit \"current\" one). For the `org` scope,\n // `scopeId` is the tenantId — discover it from `listScopes()`.\n\n /**\n * Semantic search across the agent's member scopes. Fan-out is gated\n * server-side by `listScopes` set-inclusion (fail-closed). Pass\n * `scopeType` + `scopeId` to narrow to one scope; a non-member scope\n * yields empty results (never a cross-scope leak).\n */\n async knowledgeSearch(\n query: string,\n opts?: { limit?: number; scopeType?: KnowledgeScopeType; scopeId?: string },\n ): Promise<KnowledgeSearchResult> {\n return this.request<KnowledgeSearchResult>(\"/agent/knowledge/search\", {\n method: \"POST\",\n body: JSON.stringify({\n query,\n limit: opts?.limit,\n scopeType: opts?.scopeType,\n scopeId: opts?.scopeId,\n }),\n });\n }\n\n /** Enumerate the scopes (org + teams + projects) this agent belongs to. */\n async listScopes(): Promise<{ scopes: KnowledgeScope[] }> {\n return this.request<{ scopes: KnowledgeScope[] }>(\"/agent/org/scopes\");\n }\n\n /** Read a scope's structured knowledge profile (after membership check). */\n async getScopeProfile(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n ): Promise<KnowledgeProfile> {\n return this.request<KnowledgeProfile>(\n `/agent/org/profile/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n );\n }\n\n /** List a scope's facts (non-semantic, full enumeration). */\n async listScopeFacts(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<{ facts: KnowledgeFact[]; nextCursor: string | null }> {\n const qs = new URLSearchParams();\n if (opts?.limit !== undefined) qs.set(\"limit\", String(opts.limit));\n if (opts?.cursor) qs.set(\"cursor\", opts.cursor);\n const query = qs.toString();\n return this.request<{ facts: KnowledgeFact[]; nextCursor: string | null }>(\n `/agent/org/facts/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}${query ? `?${query}` : \"\"}`,\n );\n }\n\n /**\n * Publish a fact at a scope. This is the deliberate cross-agent learning\n * boundary — facts are attributable (the agent is recorded as author) and\n * never an automatic merge. Membership is enforced server-side (403 for a\n * non-member scope).\n */\n async createScopeFact(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n text: string,\n ): Promise<KnowledgeFact> {\n return this.request<KnowledgeFact>(\n `/agent/org/facts/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n { method: \"POST\", body: JSON.stringify({ text }) },\n );\n }\n\n /** Delete a fact at a scope. */\n async deleteScopeFact(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n factId: string,\n ): Promise<{ removed: boolean }> {\n return this.request<{ removed: boolean }>(\n `/agent/org/facts/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/${encodeURIComponent(factId)}`,\n { method: \"DELETE\" },\n );\n }\n\n /** List a scope's docs (the org-files corpus; mirrored to shared/<scope>/). */\n async listScopeDocs(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<{ files: KnowledgeDoc[]; nextCursor: string | null }> {\n const qs = new URLSearchParams();\n if (opts?.limit !== undefined) qs.set(\"limit\", String(opts.limit));\n if (opts?.cursor) qs.set(\"cursor\", opts.cursor);\n const query = qs.toString();\n return this.request<{ files: KnowledgeDoc[]; nextCursor: string | null }>(\n `/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}${query ? `?${query}` : \"\"}`,\n );\n }\n\n /**\n * Read the full text of a scope doc. Resolves a presigned download URL\n * from `services/org`, then fetches the bytes directly from S3 (the one\n * legitimate raw fetch in a plugin — same pattern as sync).\n */\n async readScopeDoc(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n filePath: string,\n ): Promise<{ filePath: string; text: string }> {\n const { downloadUrl } = await this.request<{ downloadUrl: string; expiresIn: number }>(\n `/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/download/${encodeFilePath(filePath)}`,\n );\n const res = await fetch(downloadUrl, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });\n if (!res.ok) {\n await res.text();\n throw new Error(`Doc download failed (${String(res.status)})`);\n }\n const text = await res.text();\n return { filePath, text };\n }\n\n /**\n * Write (create or overwrite) a scope doc. Two-step presigned upload:\n * `services/org` returns a signed URL plus `requiredHeaders` (author /\n * authorKind / message as `x-amz-meta-*`) that MUST be sent verbatim on\n * the PUT, alongside the same `Content-Type` that was signed. Author and\n * authorKind are server-set from the agent token — never trusted here.\n */\n async writeScopeDoc(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n filePath: string,\n content: string,\n opts?: { contentType?: string; message?: string },\n ): Promise<{ filePath: string }> {\n const contentType = opts?.contentType ?? \"text/markdown\";\n const presign = await this.request<{\n uploadUrl: string;\n filePath: string;\n expiresIn: number;\n requiredHeaders: Record<string, string>;\n }>(\n `/agent/org/files/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}/upload/${encodeFilePath(filePath)}`,\n { method: \"POST\", body: JSON.stringify({ contentType, message: opts?.message }) },\n );\n\n const putHeaders = new Headers(presign.requiredHeaders);\n // The presign signed `ContentType` into the URL — the PUT must echo the\n // exact same value or S3 rejects with SignatureDoesNotMatch.\n putHeaders.set(\"Content-Type\", contentType);\n\n const res = await fetch(presign.uploadUrl, {\n method: \"PUT\",\n body: content,\n headers: putHeaders,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n await res.text();\n throw new Error(`Doc upload failed (${String(res.status)})`);\n }\n return { filePath: presign.filePath };\n }\n\n // ─── Database ───────────────────────────────────────────\n\n async registerDatabaseCredentials(): Promise<{\n connectionString: string;\n username: string;\n password: string;\n databases: string[];\n }> {\n return this.request(\"/agent/database/register\", { method: \"POST\" });\n }\n\n async reportDatabaseAudit(entry: {\n database: string;\n collection: string;\n operation: string;\n summary?: string;\n }): Promise<void> {\n await this.request(\"/agent/database/audit\", {\n method: \"POST\",\n body: JSON.stringify(entry),\n }).catch(() => {\n // Fire and forget — audit failure doesn't affect operations\n });\n }\n\n // ─── Remote (interactive relay: browser co-browse takeover) ──────\n //\n // The agent asks a human to complete a step on the live browser it's\n // looking at. `requestBrowserTakeover` creates the session and notifies\n // the user (dashboard tab + chat card + push); the plugin then blocks on\n // the relay's RELEASE_CONTROL for the actual handoff, so no long-lived\n // request is held open here. `completeRemoteSession` marks it done.\n\n async requestBrowserTakeover(args: {\n instructions: string;\n url?: string;\n conversationId?: string;\n }): Promise<{ sessionId: string; status: string }> {\n return this.request(\"/agent/remote/takeover\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async getRemoteSession(sessionId: string): Promise<RemoteSessionInfo> {\n return this.request(`/agent/remote/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async completeRemoteSession(sessionId: string): Promise<{ ok: boolean }> {\n return this.request(`/agent/remote/sessions/${encodeURIComponent(sessionId)}/complete`, {\n method: \"POST\",\n body: JSON.stringify({}),\n });\n }\n\n // ─── Voice (one-shot TTS / STT) ──────────────────────────────\n //\n // Binary siblings of `request<T>()`. `request()` forces\n // `Content-Type: application/json` and parses a `{ data: T }` envelope,\n // neither of which fits a raw-audio flow, so voice goes through `rawFetch`\n // instead. Auth (Bearer), the request budget, and the single retry on\n // transient 5xx / network errors are kept in sync with `request()`.\n\n /**\n * Issue a request that returns the raw `Response` (no JSON parsing, no\n * forced Content-Type). The caller sets `Content-Type`/`Accept` on `headers`\n * and reads the body itself (`arrayBuffer()` / `json()`).\n *\n * A single retry fires only on the same transient statuses `request()`\n * retries (authorizer-timeout 500 + LB 502/503/504) and transient network\n * errors — before the route handler runs — so re-issuing a POST does not\n * risk a duplicate side effect. Voice TTS/STT are effectively idempotent\n * (re-synthesize / re-transcribe) and meter server-side keyed on the\n * gateway requestId, so a retried transcription doesn't double-bill.\n */\n private async rawFetch(\n path: string,\n init: { method: string; headers: Headers; body?: BodyInit | Uint8Array },\n ): Promise<Response> {\n const url = `${this.apiUrl}${path}`;\n init.headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= 2; attempt++) {\n try {\n const res = await fetch(url, {\n method: init.method,\n headers: init.headers,\n // undici accepts a Uint8Array/Buffer body at runtime; the DOM\n // `BodyInit` type omits it, so widen through `unknown`.\n body: init.body as BodyInit | undefined,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n await res.text(); // drain body\n const error = new Error(`Agent API request failed (${String(res.status)})`);\n if (attempt === 1 && RETRYABLE_STATUS.has(res.status)) {\n lastError = error;\n await sleep(RETRY_DELAY_MS);\n continue;\n }\n throw error;\n }\n return res;\n } catch (err) {\n if (attempt === 1 && isRetryableNetworkError(err)) {\n lastError = err;\n await sleep(RETRY_DELAY_MS);\n continue;\n }\n throw err;\n }\n }\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n }\n\n /**\n * Text-to-speech. Returns raw PCM audio bytes plus their framing — the\n * voice service defaults to 24 kHz / mono / 16-bit. Wrap in a WAV container\n * to produce a playable file. Metered per character against the tenant\n * credit pool server-side; TTS completes regardless of metering outcome.\n */\n async tts(args: VoiceTtsArgs): Promise<VoiceTtsResult> {\n const headers = new Headers();\n headers.set(\"Content-Type\", \"application/json\");\n headers.set(\"Accept\", \"audio/pcm\");\n const res = await this.rawFetch(\"/voice/tts\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(args),\n });\n const audio = Buffer.from(await res.arrayBuffer());\n return {\n audio,\n sampleRate: parseInt(res.headers.get(\"x-sample-rate\") ?? \"24000\", 10),\n channels: parseInt(res.headers.get(\"x-channels\") ?? \"1\", 10),\n bitDepth: parseInt(res.headers.get(\"x-bit-depth\") ?? \"16\", 10),\n };\n }\n\n /**\n * Speech-to-text. Accepts raw linear16 (16-bit LE) mono PCM — NOT a WAV or\n * other container (the endpoint transcribes with a fixed linear16 encoding,\n * so a container header would be transcribed as noise). Strip any WAV header\n * and pass `sampleRate` from it before calling. Metered by transcribed\n * duration against the tenant credit pool server-side.\n */\n async stt(args: VoiceSttArgs): Promise<VoiceSttResult> {\n const headers = new Headers();\n headers.set(\"Content-Type\", \"application/octet-stream\");\n headers.set(\"x-sample-rate\", String(args.sampleRate));\n const res = await this.rawFetch(\"/voice/stt\", {\n method: \"POST\",\n headers,\n body: args.audio,\n });\n const body = (await res.json()) as { data: VoiceSttResult };\n return body.data;\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/agent-api-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Agent self-service API client — agents calling /agents/ endpoints",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {},
|
|
19
19
|
"license": "UNLICENSED",
|
|
20
|
+
"homepage": "https://alfe.ai",
|
|
21
|
+
"author": "Alfe (https://alfe.ai)",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"alfe",
|
|
24
|
+
"ai-agents",
|
|
25
|
+
"agent",
|
|
26
|
+
"llm",
|
|
27
|
+
"sdk"
|
|
28
|
+
],
|
|
20
29
|
"scripts": {
|
|
21
30
|
"build": "tsdown",
|
|
22
31
|
"dev": "tsdown --watch",
|