@alfe.ai/agent-api-client 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/tool-error-capture.ts","../src/transport.ts","../src/domains/chat.ts","../src/domains/connect-credentials.ts","../src/domains/database.ts","../src/domains/identity.ts","../src/domains/images.ts","../src/domains/integrations.ts","../src/domains/knowledge.ts","../src/domains/memory.ts","../src/domains/mobile.ts","../src/domains/remote.ts","../src/domains/search.ts","../src/domains/secrets.ts","../src/domains/self.ts","../src/domains/voice.ts","../src/domains/sync.ts","../src/domains/teams.ts","../src/domains/workspace.ts","../src/domains/webhooks.ts","../src/index.ts"],"sourcesContent":["/**\n * Tool-error capture for Alfe OpenClaw plugins.\n *\n * OpenClaw converts a thrown tool handler into a model-facing `tool_result`\n * WITHOUT logging, and most Alfe plugins catch-and-return an error result the\n * same silent way — so tool failures never appear in the runtime's output and\n * therefore never reach Sentry (the gateway daemon supervises the OpenClaw\n * process and reports error-looking output lines to the `agent-runtime`\n * project — see packages/gateway/src/runtime-output-monitor.ts).\n *\n * `installToolErrorCapture(api, { plugin })` closes that gap at the ONE choke\n * point every plugin already has: it wraps `api.registerTool` so every tool's\n * `execute` emits a deterministic, detector-matched line on failure:\n *\n * [ERROR] alfe-tool plugin=<plugin> tool=<name> <thrown|result-error>: <msg> (at <first-frame>)\n *\n * The `[ERROR]` prefix at line start is exactly what the daemon's\n * `ErrorLineDetector` classifies as an error-log block, so the failure lands\n * in Sentry fingerprinted by its normalized message — no Sentry SDK inside\n * the plugin process, no new dependency. Behavior toward OpenClaw and the\n * model is UNCHANGED: throws are rethrown, results returned as-is.\n */\n\n/**\n * Minimal shape of the OpenClaw plugin api this helper relies on. Method\n * syntax on purpose — TS checks method signatures bivariantly, so each\n * plugin's own concretely-typed `registerTool(tool: ToolDef): void` is\n * accepted without casts.\n */\nexport interface ToolCaptureApi {\n registerTool(...args: never[]): unknown;\n}\n\nexport interface InstallToolErrorCaptureOptions {\n /** Plugin package short-name for attribution (e.g. \"openclaw-secrets\"). */\n plugin: string;\n /**\n * Line sink — defaults to writing `process.stderr` directly (the plugin\n * runs in-process in OpenClaw, so this lands on the runtime's stderr, which\n * the daemon supervises — and a console patch can't reformat it away).\n * Injectable for tests.\n */\n emit?: (line: string) => void;\n}\n\ninterface ToolLike {\n name?: unknown;\n execute?: unknown;\n}\n\n// `Symbol.for` so the markers survive duplicate module copies (dual ESM/CJS\n// builds, multiple plugins bundling their own helper copy) — installing twice\n// or re-registering a module-level tool singleton must not stack wrappers.\nconst INSTALLED_MARKER = Symbol.for('alfe.toolErrorCapture.installed');\nconst WRAPPED_MARKER = Symbol.for('alfe.toolErrorCapture.wrapped');\n\n/** `isError: true` (MCP/Anthropic convention) or `details.status: 'error'` (OpenClaw). */\nfunction readResultErrorMessage(result: unknown): string | null {\n if (typeof result !== 'object' || result === null) return null;\n const r = result as {\n isError?: unknown;\n details?: { status?: unknown; error?: unknown };\n content?: { type?: unknown; text?: unknown }[];\n };\n const flagged = r.isError === true || r.details?.status === 'error';\n if (!flagged) return null;\n if (typeof r.details?.error === 'string') return r.details.error;\n if (Array.isArray(r.content)) {\n for (const item of r.content) {\n if (item.type === 'text' && typeof item.text === 'string') return item.text;\n }\n }\n return '(no error text)';\n}\n\n/** First stack frame, for inline context without emitting a multi-line block. */\nfunction firstFrame(err: unknown): string {\n if (!(err instanceof Error) || !err.stack) return '';\n const frame = err.stack.split('\\n').find((l) => l.trimStart().startsWith('at '));\n return frame ? ` (${frame.trim()})` : '';\n}\n\nfunction buildLine(\n plugin: string,\n tool: string,\n kind: 'thrown' | 'result-error',\n message: string,\n frame = '',\n): string {\n // Single line, [ERROR]-prefixed at position 0 — matched by the gateway\n // detector's LOG_ERROR_LEVEL as a one-line error-log block. Keep under the\n // detector's 500-char line cap.\n const safeToken = (value: string) => value.replace(/[^A-Za-z0-9_.@/-]+/g, '_').slice(0, 80);\n const stripControls = (value: string) =>\n Array.from(value, (character) => {\n const code = character.charCodeAt(0);\n return code < 32 || (code >= 127 && code <= 159) ? ' ' : character;\n }).join('');\n const oneLine = stripControls(message).replace(/\\s+/g, ' ').trim();\n const safeFrame = stripControls(frame).replace(/\\s+/g, ' ');\n return `[ERROR] alfe-tool plugin=${safeToken(plugin)} tool=${safeToken(tool)} ${kind}: ${oneLine}${safeFrame}`.slice(0, 480);\n}\n\nfunction wrapExecute(\n tool: ToolLike,\n opts: Required<Pick<InstallToolErrorCaptureOptions, 'plugin' | 'emit'>>,\n): void {\n const execute = tool.execute;\n if (typeof execute !== 'function') return;\n // Idempotent: plugins that re-register module-level tool singletons per\n // session would otherwise accrue a wrapper layer (K emissions per failure).\n const marked = tool as ToolLike & { [WRAPPED_MARKER]?: boolean };\n if (marked[WRAPPED_MARKER]) return;\n marked[WRAPPED_MARKER] = true;\n const name = typeof tool.name === 'string' ? tool.name : '(unnamed)';\n tool.execute = async (...args: unknown[]) => {\n try {\n const result: unknown = await (execute as (...a: unknown[]) => unknown).apply(tool, args);\n const resultError = readResultErrorMessage(result);\n if (resultError !== null) {\n try {\n opts.emit(buildLine(opts.plugin, name, 'result-error', resultError));\n } catch {\n /* capture must never affect the tool result */\n }\n }\n return result;\n } catch (err) {\n try {\n const message = err instanceof Error ? err.message : String(err);\n opts.emit(buildLine(opts.plugin, name, 'thrown', message, firstFrame(err)));\n } catch {\n /* capture must never mask the original error */\n }\n throw err;\n }\n };\n}\n\n/**\n * Wrap `api.registerTool` so every tool registered AFTER this call gets\n * failure capture. Handles both OpenClaw registration signatures:\n * `registerTool(toolDef)` and `registerTool((ctx) => toolDef, opts)`.\n * Call once, first thing in the plugin's `activate`/`register` entry.\n * Never throws.\n */\nexport function installToolErrorCapture(\n api: ToolCaptureApi,\n options: InstallToolErrorCaptureOptions,\n): void {\n try {\n const markedApi = api as ToolCaptureApi & { [INSTALLED_MARKER]?: boolean };\n if (markedApi[INSTALLED_MARKER]) return;\n // Write the supervised fd directly — console.error survives stream\n // re-routing but not a console patch that reformats lines (a JSON level\n // field or long prefix would break the detector's [ERROR]-prefix match).\n const emit =\n options.emit ?? ((line: string) => { process.stderr.write(`${line}\\n`); });\n const opts = { plugin: options.plugin, emit };\n const original = api.registerTool.bind(api) as (...args: unknown[]) => unknown;\n const wrappedRegisterTool = (...args: unknown[]) => {\n let preparedArgs = args;\n try {\n const [first, ...rest] = args;\n if (typeof first === 'function') {\n // Factory signature: wrap the tool the factory produces.\n const factory = first as (...fa: unknown[]) => unknown;\n const wrappedFactory = (...fa: unknown[]) => {\n const tool = factory(...fa);\n if (typeof tool === 'object' && tool !== null) {\n try {\n wrapExecute(tool as ToolLike, opts);\n } catch {\n /* register the original tool if it is immutable */\n }\n }\n return tool;\n };\n preparedArgs = [wrappedFactory, ...rest];\n }\n if (typeof first === 'object' && first !== null) {\n wrapExecute(first as ToolLike, opts);\n }\n } catch {\n // Wrapping failed for this call — register the original arguments.\n preparedArgs = args;\n }\n // Registration itself is intentionally outside the best-effort wrapper\n // catch. If OpenClaw rejects a tool, calling registerTool a second time\n // can duplicate partial side effects and obscures the original failure.\n return original(...preparedArgs);\n };\n api.registerTool = wrappedRegisterTool;\n markedApi[INSTALLED_MARKER] = true;\n } catch {\n /* capture install must never break plugin activation */\n }\n}\n","/**\n * Shared HTTP transport for the Agent API client — request core, retry\n * policy, error formatting, and the `ApiBase` class the domain method\n * groups under `./domains/` build on.\n */\n\nexport interface AgentApiClientConfig {\n apiKey: string;\n apiUrl: 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 */\nexport function encodeFilePath(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\n/**\n * Build the thrown Error message for a non-2xx response. The\n * @alfe/api-core error envelope carries the server's detail as\n * `{ message }` (Zod failures add `{ issues }`); some handlers use\n * `{ error }`. Surfacing that detail matters for tool-facing callers —\n * e.g. GET /mobile/numbers 404s with \"No phone number assigned…\n * use mobile_search_numbers\", which guides the agent's next tool call.\n */\nfunction formatErrorMessage(status: number, rawBody: string): string {\n const prefix = `Agent API request failed (${String(status)})`;\n try {\n const json = JSON.parse(rawBody) as Record<string, unknown>;\n const issues = json.issues as { path?: string[]; message?: string }[] | undefined;\n if (Array.isArray(issues) && issues.length > 0) {\n const details = issues\n .map((i) => `${i.path?.join(\".\") ?? \"input\"}: ${i.message ?? \"invalid\"}`)\n .join(\"; \");\n return `${prefix}: validation failed — ${details}`;\n }\n const detail = typeof json.error === \"string\"\n ? json.error\n : typeof json.message === \"string\"\n ? json.message\n : undefined;\n if (detail) return `${prefix}: ${detail}`;\n } catch {\n // Non-JSON error body — fall back to the bare status message.\n }\n return prefix;\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).\nexport const 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 isSafeRetryMethod(method: string | undefined): boolean {\n const normalized = (method ?? \"GET\").toUpperCase();\n return normalized === \"GET\" || normalized === \"HEAD\" || normalized === \"OPTIONS\";\n}\n\nexport function 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/** Whether a completed request may be retried/polled without masking a real client error. */\nexport function isTransientRequestError(err: unknown): boolean {\n if (isRetryableNetworkError(err)) return true;\n const status = (err as { status?: unknown } | null)?.status;\n return typeof status === \"number\" && RETRYABLE_STATUS.has(status);\n}\n\nexport class AgentApiTransport {\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 /**\n * Binary sibling 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 (voice TTS/STT), so those go\n * through this instead. Auth (Bearer), the request budget, and the single\n * retry policy on transient 5xx / network errors is kept in sync with\n * `request()`. Safe read methods retry once by default; mutation methods do\n * not, because a response can be lost after a handler or provider call has\n * already succeeded.\n */\n async rawRequest(\n path: string,\n init: { method: string; headers: Headers; body?: BodyInit | Uint8Array },\n extra?: { retry?: boolean },\n ): Promise<Response> {\n const url = `${this.apiUrl}${path}`;\n init.headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n\n const retry = extra?.retry ?? isSafeRetryMethod(init.method);\n const maxAttempts = retry ? 2 : 1;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; 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 the cast.\n body: init.body as BodyInit | undefined,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n const errorBody = await res.text(); // drain body\n const error = new Error(formatErrorMessage(res.status, errorBody)) as Error & {\n status?: number;\n };\n error.status = res.status;\n if (attempt < maxAttempts && 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 < maxAttempts && 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 * @param extra.timeoutMs Per-request abort timeout (default REQUEST_TIMEOUT_MS).\n * Long endpoints (image generation) pass a larger value so the gateway's\n * own timeout wins with a readable status instead of a client-side abort.\n * @param extra.retry Whether to retry once on transient failures. Safe reads\n * (GET/HEAD/OPTIONS) default to true; mutations default to false. Set true\n * only when the endpoint's server-side contract is explicitly idempotent.\n * @param extra.signal Optional caller cancellation combined with the client's\n * own timeout budget. Aborting either signal cancels the request.\n */\n async request<T>(\n path: string,\n options?: RequestInit,\n extra?: { timeoutMs?: number; retry?: boolean; signal?: AbortSignal },\n ): 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 const timeoutMs = extra?.timeoutMs ?? REQUEST_TIMEOUT_MS;\n const retry = extra?.retry ?? isSafeRetryMethod(options?.method);\n const maxAttempts = retry ? 2 : 1;\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 // A 500 may also come from the route after a side effect has committed, and\n // a network failure can happen after request bytes reached the server.\n // Mutation methods therefore opt out unless a caller explicitly asserts a\n // server-side idempotency contract (the Connect token-refresh methods do).\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const res = await fetch(url, {\n ...options,\n headers,\n signal: extra?.signal\n ? AbortSignal.any([extra.signal, AbortSignal.timeout(timeoutMs)])\n : AbortSignal.timeout(timeoutMs),\n });\n\n if (!res.ok) {\n const errorBody = await res.text(); // drain body\n const error = new Error(formatErrorMessage(res.status, errorBody)) as Error & {\n status?: number;\n };\n error.status = res.status;\n if (attempt < maxAttempts && 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 < maxAttempts && 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/**\n * Base class for the domain method groups. Holds the shared transport;\n * `AgentApiClient` assembles the groups onto one class via `applyMixins`\n * (prototype copy), so methods keep their original `this`-on-the-client\n * call shape.\n */\nexport class ApiBase {\n protected readonly transport: AgentApiTransport;\n\n constructor(transport: AgentApiTransport) {\n this.transport = transport;\n }\n}\n","/**\n * Chat attachment + activity methods for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\nexport class ChatApi extends ApiBase {\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.transport.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.transport.request<{ recorded: boolean }>(\"/agent/activity\", {\n method: \"POST\",\n body: JSON.stringify(data),\n });\n }\n}\n","/**\n * services/connect credential + account methods (Google, GitHub, Xero,\n * Notion, Atlassian, MYOB, Salesforce, Microsoft 365) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\nexport class ConnectCredentialsApi extends ApiBase {\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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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 * `xeroTenantId` is the model-facing organisation selector. The separate\n * `accountIdentifier` is the Connect persistence key used for refresh and\n * may be an email; never substitute one for the other.\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.transport.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 // accountIdentifier may be the user's email when one OAuth grant\n // exposes multiple Xero organisations. It is not a tenant selector.\n // Preserve the absence so Pattern A consumers fail closed instead of\n // presenting an email as authority for an arbitrary first tenant.\n xeroTenantId: a.xeroTenantId ?? \"\",\n })),\n };\n }\n\n async refreshXeroToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.transport.request(\n \"/agent/connect/xero/refresh\",\n { method: \"POST\" },\n { retry: true },\n );\n }\n\n /**\n * Refresh a specific Xero Connection by its exact `accountIdentifier` from\n * `getXeroAccounts()`. Do not substitute `xeroTenantId`: current Xero OAuth\n * rows may use the account email as their persistence key even when a sole\n * organisation tenant ID is available in provider metadata.\n */\n async refreshXeroAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/xero/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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.transport.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.transport.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.transport.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.transport.request(\n \"/agent/connect/atlassian/refresh\",\n { method: \"POST\" },\n { retry: true },\n );\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.transport.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.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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.transport.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.transport.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.transport.request(\n \"/agent/connect/myob/refresh\",\n { method: \"POST\" },\n { retry: true },\n );\n }\n\n /**\n * Pattern A: refresh one MYOB Connection by its stable\n * `accountIdentifier` (the MYOB business id returned by\n * `getMYOBAccounts()`).\n *\n * MYOB refresh tokens belong to individual Connection rows. A\n * multi-business client must use this method instead of refreshing the\n * primary Connection and copying that access token into every cached\n * business client.\n */\n async refreshMYOBAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/myob/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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. 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.transport.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.transport.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.transport.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\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 email: string;\n microsoftTenantId: string;\n workspaceDomain: string;\n }[];\n }> {\n const raw = await this.transport.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n accessTokenExpiresAt?: 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 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.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * Disconnects one connected Microsoft 365 account for the agent, by its\n * `accountIdentifier`. Hits the generic per-account disconnect route\n * (`DELETE /agent/connect/microsoft/accounts/{accountIdentifier}`), which\n * resolves across the agent's full effective scope chain and deletes the\n * matching Connection row. Returns the remaining accounts.\n *\n * IMPORTANT: pass the `accountIdentifier` from `getMicrosoftAccounts()`, NOT\n * a synthesised email. For Microsoft, `accountIdentifier` is the user's email\n * only when the Graph profile fetch succeeded at connect time; it falls back\n * to the Azure tenant id (`tid` claim) otherwise. The backend matches on\n * `accountIdentifier` exactly, so passing an email would 404 on those\n * fallback-identifier accounts. (This is why the param is not named `email`,\n * unlike `disconnectGoogleAccount` where the identifier is always the email.)\n */\n async disconnectMicrosoftAccount(accountIdentifier: string): Promise<{\n accounts: { accountIdentifier: string; displayName?: string; connectedAt?: string }[];\n }> {\n const raw = await this.transport.request<{\n accounts: { accountIdentifier: string; displayName?: string | null; connectedAt?: string }[];\n }>(`/agent/connect/microsoft/accounts/${encodeURIComponent(accountIdentifier)}`, {\n method: \"DELETE\",\n });\n return {\n accounts: raw.accounts.map((a) => ({\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName ?? undefined,\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n /**\n * Resolve the primary cTrader Connection's credentials for the calling\n * agent. Unlike most providers, the cTrader Open API needs app-level auth\n * (`clientId` + `clientSecret`) AND account auth (`accessToken` +\n * `accountId`) on the socket, so `@alfe.ai/ctrader-mcp` self-fetches the\n * full set here at startup (the atlassian/google pattern). `clientId` /\n * `clientSecret` are the SST-sourced global app credentials the connect\n * endpoint injects — they are never persisted on the connection. `host` is\n * the resolved TLS endpoint (`live.ctraderapi.com` / `demo.ctraderapi.com`)\n * derived from the selected account's live/demo flag.\n */\n async getCTraderCredentials(): Promise<{\n accessToken: string;\n refreshToken: string;\n accountId: string;\n host: string;\n clientId: string;\n clientSecret: string;\n }> {\n const raw = await this.transport.request<{\n accessToken?: string;\n refreshToken?: string;\n accountId?: string | number;\n host?: string;\n clientId?: string;\n clientSecret?: string;\n }>(\"/agent/connect/ctrader/credentials\");\n return {\n accessToken: raw.accessToken ?? \"\",\n refreshToken: raw.refreshToken ?? \"\",\n accountId: raw.accountId != null ? String(raw.accountId) : \"\",\n host: raw.host ?? \"\",\n clientId: raw.clientId ?? \"\",\n clientSecret: raw.clientSecret ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for cTrader.\n *\n * Unlike atlassian/salesforce (one Connection row per account/site), a\n * cTrader is MULTI-grant per agent: an agent may connect several distinct\n * cTrader logins, each its own Connection row keyed on `accountIdentifier =\n * ctid:<userId>` (Phase 1). This aggregates the *trading accounts* across\n * ALL of those Connection rows — each row contributes its `availableAccounts`\n * flattened, and every account carries ITS OWN grant's `accessToken` (the\n * token that authenticates that account against the cTrader Open API). One\n * OAuth grant still covers all accounts under that single login on one shared\n * token; only the `ctidTraderAccountId` and the protobuf socket `host` (live\n * vs demo) differ within a grant. Across grants the tokens differ, so the\n * token is now PER-ACCOUNT rather than hoisted to the top level.\n *\n * `host` per account is derived from the account's `isLive` flag\n * (`live.ctraderapi.com` / `demo.ctraderapi.com`) — the same mapping the\n * connect provider applies server-side when an account is auto-selected.\n *\n * `clientId` / `clientSecret` are the SST-sourced GLOBAL app credentials the\n * connect endpoint injects — identical across every Connection row (one\n * cTrader app), never persisted on a connection. We take them from the first\n * row that carries them.\n *\n * Accounts are deduped on `ctidTraderAccountId` first-wins: Spotware ids are\n * globally unique across logins, so a duplicate can only appear if the same\n * account somehow surfaced under two grants — first-wins keeps it\n * deterministic.\n *\n * `accounts` may be empty (no cTrader Connection at all), in which case we\n * return empty creds rather than throwing.\n */\n async getCTraderAccounts(): Promise<{\n accounts: {\n ctidTraderAccountId: string;\n host: string;\n isLive: boolean;\n brokerName?: string;\n accountNumber?: string;\n accessToken: string;\n /**\n * The stable per-grant Connection key (`ctid:<userId>`) this account\n * belongs to. Every trading account under one cTrader login shares one\n * grant (one OAuth token), so this is the identifier the MCP server\n * passes to `refreshCTraderAccount()` to rotate the token for the whole\n * grant on a `CH_ACCESS_TOKEN_INVALID` expiry. Empty string when the\n * server did not supply one (legacy rows) — such an account can still\n * trade with its current token but cannot self-refresh.\n */\n accountIdentifier: string;\n }[];\n clientId: string;\n clientSecret: string;\n }> {\n const raw = await this.transport.request<{\n provider: string;\n accounts: {\n accessToken?: string;\n clientId?: string;\n clientSecret?: string;\n accountIdentifier?: string;\n availableAccounts?: {\n ctidTraderAccountId?: string | number;\n accountId?: string | number;\n isLive?: boolean;\n brokerName?: string;\n accountNumber?: string | number;\n }[];\n }[];\n }>(\"/agent/connect/ctrader/accounts\");\n\n // No cTrader Connection rows → return empty creds rather than throwing.\n // `raw.accounts` is `T[]`, so an explicit length guard is how we model the\n // empty case (no noUncheckedIndexedAccess).\n if (raw.accounts.length === 0) {\n return { accounts: [], clientId: \"\", clientSecret: \"\" };\n }\n\n // Global app credentials are identical across rows (one cTrader app). Take\n // them from the first row that supplies them; guard the empty case.\n let clientId = \"\";\n let clientSecret = \"\";\n for (const row of raw.accounts) {\n if (!clientId && row.clientId) clientId = row.clientId;\n if (!clientSecret && row.clientSecret) clientSecret = row.clientSecret;\n if (clientId && clientSecret) break;\n }\n\n // Aggregate across ALL grant rows. Each account carries the row's own\n // `accessToken`. Dedup on ctidTraderAccountId first-wins.\n const seen = new Set<string>();\n const accounts: {\n ctidTraderAccountId: string;\n host: string;\n isLive: boolean;\n brokerName?: string;\n accountNumber?: string;\n accessToken: string;\n accountIdentifier: string;\n }[] = [];\n for (const row of raw.accounts) {\n const rowToken = row.accessToken ?? \"\";\n // The grant key (`ctid:<userId>`) is shared by every account under this\n // Connection row; stamp it onto each flattened account so the consumer\n // can refresh the whole grant's token by its exact accountIdentifier.\n const rowAccountIdentifier = row.accountIdentifier ?? \"\";\n for (const a of row.availableAccounts ?? []) {\n const id =\n a.ctidTraderAccountId != null\n ? String(a.ctidTraderAccountId)\n : a.accountId != null\n ? String(a.accountId)\n : \"\";\n if (id.length === 0 || seen.has(id)) continue;\n seen.add(id);\n const isLive = a.isLive === true;\n accounts.push({\n ctidTraderAccountId: id,\n host: isLive ? \"live.ctraderapi.com\" : \"demo.ctraderapi.com\",\n isLive,\n ...(a.brokerName != null ? { brokerName: a.brokerName } : {}),\n ...(a.accountNumber != null\n ? { accountNumber: String(a.accountNumber) }\n : {}),\n accessToken: rowToken,\n accountIdentifier: rowAccountIdentifier,\n });\n }\n }\n\n return { accounts, clientId, clientSecret };\n }\n\n /**\n * Pattern A: refresh a specific cTrader grant by its stable\n * `accountIdentifier` (`ctid:<userId>` from `getCTraderAccounts()`).\n *\n * cTrader access tokens live ~30 days; the `getCTraderAccounts()` /\n * credentials reads serve the STORED token without refreshing, so refresh is\n * the consumer's job. `@alfe.ai/ctrader-mcp` calls this when the cTrader Open\n * API rejects an account-auth with `CH_ACCESS_TOKEN_INVALID`, then re-runs\n * the socket handshake with the returned `accessToken`.\n *\n * Refreshing one grant rotates the single OAuth token that covers EVERY\n * trading account under that login. cTrader's refresh token itself does not\n * expire but may rotate on refresh (`rotatesRefreshToken: true`); connect\n * persists the rotated refresh token server-side, so the caller only needs\n * the new `accessToken`. Mirrors `refreshXeroAccountToken`.\n */\n async refreshCTraderAccount(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/ctrader/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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. Use\n * `getShopifyAccounts()` for the multi-account shape required by Pattern A\n * (`@alfe.ai/shopify-mcp` keys per-shop on the myshopify domain).\n */\n async getShopifyCredentials(): Promise<{\n accessToken: string;\n shopDomain: string;\n shopGid: string;\n shopName: string;\n apiVersion: string;\n }> {\n const raw = await this.transport.request<{\n accessToken: string;\n shopDomain?: string;\n shopGid?: string;\n shopName?: string;\n apiVersion?: string;\n }>(\"/agent/connect/shopify/credentials\");\n return {\n accessToken: raw.accessToken,\n shopDomain: raw.shopDomain ?? \"\",\n shopGid: raw.shopGid ?? \"\",\n shopName: raw.shopName ?? \"\",\n apiVersion: raw.apiVersion ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Shopify. Returns every\n * agent-scoped Shopify Connection. One OAuth grant maps to one store, so the\n * stable per-call selector is the store's myshopify domain (`shopDomain`),\n * NOT `accountIdentifier` — the connect provider keys `accountIdentifier` on\n * the immutable shop GID (falling back to the domain), so `shopDomain` is the\n * value the LLM passes and the plugin routes on.\n *\n * Each entry is shaped by the connect provider's `buildCredentialsResponse`:\n * `{ accessToken, shopDomain, shopGid, shopName, apiVersion }` — offline\n * Shopify tokens never expire, so there is NO token / expiry field and no\n * refresh method (unlike Salesforce). The GraphQL Admin API authenticates\n * purely on `X-Shopify-Access-Token`; no client credentials are on the wire.\n */\n async getShopifyAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n shopDomain: string;\n shopGid: string;\n shopName: string;\n apiVersion: string;\n }[];\n }> {\n const raw = await this.transport.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n shopDomain?: string;\n shopGid?: string;\n shopName?: string;\n apiVersion?: string;\n }[];\n }>(\"/agent/connect/shopify/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 // `shopDomain` is the myshopify host the GraphQL/token requests target.\n // Fall back to `accountIdentifier` only when it already IS the domain\n // (the connect provider uses the domain as the identifier when the\n // shop-info fetch couldn't resolve a GID).\n shopDomain: a.shopDomain ?? \"\",\n shopGid: a.shopGid ?? \"\",\n shopName: a.shopName ?? \"\",\n apiVersion: a.apiVersion ?? \"\",\n })),\n };\n }\n\n /**\n * Pattern A: provider-parameterized multi-account credential fetch for the\n * social connectors (Bluesky, and the approval-gated backlog: X, Meta,\n * Threads, LinkedIn, Pinterest, TikTok, Reddit, YouTube).\n *\n * Unlike the bespoke `getGithubAccounts()` / `getXeroAccounts()` shapes,\n * this returns a UNIFORM normalized account shape so `@alfe.ai/social-mcp`'s\n * shared driver can require a single `account` selector on every\n * credential-touching tool regardless of platform. The backend\n * `api-agents/{provider}/accounts` route is already provider-generic; this\n * is the client-side normalization the plan (`do-we-need-any-moonlit-toucan`\n * Phase 0, step 5) calls for.\n *\n * `accountIdentifier` is the stable per-account selector the LLM should\n * pass back (for Bluesky: the account DID). `accessToken` carries whatever\n * the provider's `buildCredentialsResponse` bundles (for Bluesky: the JSON\n * session bundle — the driver parses the `accessJwt` out of it, or reads the\n * top-level `accessJwt` from `providerMetadata`-adjacent fields). Everything\n * else the driver needs for routing (handle, pdsHost, did, …) is on\n * `providerMetadata`.\n *\n * Token refresh is delegated to connect (never done in-plugin) via the\n * per-account route `POST /agent/connect/{provider}/accounts/{accountIdentifier}/refresh`\n * — call `refreshSocialAccount(provider, accountIdentifier)`. (The non-account\n * `POST /agent/connect/{provider}/refresh` route refreshes the provider's\n * PRIMARY connection, which is wrong under multi-account Pattern A.)\n */\n async getSocialAccounts(provider: string): Promise<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n accessToken: string;\n providerMetadata: Record<string, unknown>;\n connectedAt: string;\n }[];\n }> {\n const raw = await this.transport.request<{\n provider?: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n accessToken?: string;\n providerMetadata?: Record<string, unknown>;\n connectedAt: string;\n }[];\n }>(`/agent/connect/${encodeURIComponent(provider)}/accounts`);\n return {\n provider: raw.provider ?? provider,\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n accessToken: a.accessToken ?? \"\",\n providerMetadata: a.providerMetadata ?? {},\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n /**\n * Pattern A: refresh a specific social Connection by its stable\n * `accountIdentifier` (for Bluesky: the account DID) via the\n * provider-generic per-account refresh route. The counterpart to\n * `getSocialAccounts(provider)`; `@alfe.ai/social-mcp` calls this on a\n * 401/ExpiredToken from the platform PDS/API, then re-fetches accounts to\n * pick up the rotated bundle.\n *\n * Refresh itself is ALWAYS delegated to connect — the plugin never calls\n * the platform's own refresh XRPC (e.g. `com.atproto.server.refreshSession`)\n * because connect owns the encrypted refresh token + rotation persistence\n * (Bluesky rotates the refreshJwt; a missed rotation kills the connection\n * after one refresh). The returned `accessToken` is whatever the provider's\n * `refreshToken` hook re-bundled (for Bluesky: the JSON session bundle with\n * the fresh `accessJwt`) — callers typically ignore it and re-fetch via\n * `getSocialAccounts` for a consistent shape.\n */\n async refreshSocialAccount(\n provider: string,\n accountIdentifier: string,\n ): Promise<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/${encodeURIComponent(provider)}/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\n return {\n accountIdentifier: raw.accountIdentifier,\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n}\n","/**\n * Per-tenant MongoDB methods (services/database) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Database ───────────────────────────────────────────\n\nexport class DatabaseApi extends ApiBase {\n async registerDatabaseCredentials(): Promise<{\n connectionString: string;\n username: string;\n password: string;\n databases: string[];\n }> {\n return this.transport.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.transport.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","/**\n * Identity resolution, verification, and CRM methods for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Identity ─────────────────────────────────────────────\n//\n// Identity resolution, permission enforcement, and CRM tools.\n// The agent API derives tenantId + agentId from the agent token.\n\nexport class IdentityApi extends ApiBase {\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.transport.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.transport.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.transport.request(`/agent/identity/search${query ? `?${query}` : \"\"}`);\n }\n\n async getIdentityContext(identityId: string): Promise<{\n context: unknown;\n }> {\n return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/context`);\n }\n\n async mergeIdentities(\n survivorId: string,\n args: { mergedId: string },\n ): Promise<{ ok: boolean; error?: string }> {\n return this.transport.request(`/agent/identity/${encodeURIComponent(survivorId)}/merge`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async unmergeIdentity(identityId: string): Promise<{ ok: boolean; error?: string }> {\n return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/unmerge`, {\n method: \"POST\",\n });\n }\n\n async addIdentityNote(identityId: string, args: {\n content: string;\n category?: string;\n }): Promise<{ noteId: string | null }> {\n return this.transport.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 }): Promise<{ ok: boolean }> {\n return this.transport.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 cursor?: string;\n }): Promise<{ entries: unknown[]; cursor: string | null }> {\n const qs = new URLSearchParams();\n if (args?.limit) qs.set(\"limit\", String(args.limit));\n if (args?.cursor) qs.set(\"cursor\", args.cursor);\n const query = qs.toString();\n return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/changelog${query ? `?${query}` : \"\"}`);\n }\n\n async rollbackIdentity(identityId: string, args: {\n targetVersion: number;\n }): Promise<{ ok: boolean; entry?: unknown }> {\n return this.transport.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.transport.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\" | \"already_confirmed\";\n error?: string;\n }> {\n return this.transport.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.transport.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.transport.request(\"/agent/google/resolve-sender\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n}\n","/**\n * Image-generation method — text prompt → a stable, public CDN image URL.\n * Ported from main's monolith addition into the domain-split layout.\n */\nimport { ApiBase, isTransientRequestError, sleep } from \"../transport.js\";\n\n// Async image-generation job polling. Generation runs off-request on a job\n// worker (no API Gateway 30s ceiling), so the client enqueues then polls.\n// The plugin runs in the daemon (not a Lambda), so a multi-minute poll is fine.\nconst IMAGE_POLL_INTERVAL_MS = 2_000;\nconst IMAGE_JOB_TIMEOUT_MS = 180_000;\n\nexport class ImagesApi extends ApiBase {\n /**\n * Generate an image from a text prompt and get back a STABLE, public URL\n * (served from the agent-assets CDN — it does not expire). Embed the returned\n * `imageUrl` in a reply as markdown to show it to the user.\n *\n * ASYNC: `gpt-image-1` routinely runs 30–60s, which exceeds the API Gateway\n * 30s ceiling, so this enqueues a job (`POST /agent/images/generate` →\n * `jobId`) then polls (`GET /agent/images/{jobId}`) until it completes. The\n * worker's real failure message (e.g. an unsupported `size`) surfaces via the\n * job's `error` field.\n */\n async generateImage(args: {\n prompt: string;\n model?: string;\n size?: string;\n quality?: string;\n }): Promise<{ imageUrl: string; model: string }> {\n // Enqueue via a NON-retrying POST. `scheduleJob` mints a fresh job id per\n // call (not idempotent), so a retried enqueue could double-enqueue → two\n // metered generations. A lost enqueue response must fail clean, not retry.\n const { jobId } = await this.transport.request<{ jobId: string }>(\n \"/agent/images/generate\",\n { method: \"POST\", body: JSON.stringify(args) },\n { retry: false },\n );\n\n // Poll until the worker finishes. A transient poll error is TOLERATED — the\n // job keeps running (and bills) server-side, so a blip must not abandon it;\n // only a terminal `failed` or the deadline ends the wait.\n const deadline = Date.now() + IMAGE_JOB_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await sleep(IMAGE_POLL_INTERVAL_MS);\n let job: {\n status: \"pending\" | \"running\" | \"completed\" | \"failed\";\n imageUrl?: string;\n model?: string;\n error?: string;\n };\n try {\n job = await this.transport.request(`/agent/images/${jobId}`);\n } catch (error) {\n if (isTransientRequestError(error)) continue;\n throw error;\n }\n\n if (job.status === \"completed\") {\n if (!job.imageUrl) throw new Error(\"Image generation completed without a URL\");\n return { imageUrl: job.imageUrl, model: job.model ?? args.model ?? \"gpt-image-1\" };\n }\n if (job.status === \"failed\") {\n const detail = job.error ? `: ${job.error.split(\"\\n\")[0]}` : \"\";\n throw new Error(`Image generation failed${detail}`);\n }\n // pending | running → keep polling\n }\n throw new Error(\"Image generation timed out\");\n }\n}\n","/**\n * Integration lifecycle, OAuth, and registry methods for the Agent API client.\n */\n\nimport type {\n IntegrationInstall,\n IntegrationConfigResult,\n RegistryEntry,\n} from \"@alfe/types\";\nimport { ApiBase } from \"../transport.js\";\n\nexport class IntegrationsApi extends ApiBase {\n async listIntegrations(): Promise<IntegrationInstall[]> {\n return this.transport.request<IntegrationInstall[]>(\"/agent/integrations\");\n }\n\n async getIntegrationConfig(integrationId: string): Promise<IntegrationConfigResult> {\n try {\n return await this.transport.request<IntegrationConfigResult>(\n `/agent/integrations/${encodeURIComponent(integrationId)}/config`,\n );\n } catch (err) {\n // A 404 means the integration simply isn't installed for this agent —\n // an expected answer, not a failure. Return it as data so the LLM sees\n // `installed: false` instead of a surfaced tool error (Sentry RUNTIME-1).\n if ((err as { status?: number }).status === 404) {\n return { integrationId, config: {}, configSchema: [], installed: false };\n }\n throw err;\n }\n }\n\n async updateIntegrationConfig(\n integrationId: string,\n config: Record<string, unknown>,\n ): Promise<void> {\n await this.transport.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.transport.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.transport.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.transport.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.transport.request(\n `/agent/integrations/oauth/status?provider=${encodeURIComponent(provider)}`,\n );\n }\n\n async getRegistry(): Promise<{ integrations: RegistryEntry[] }> {\n return this.transport.request<{ integrations: RegistryEntry[] }>(\"/integrations/registry\");\n }\n}\n","/**\n * Knowledge resource methods (org/team/project scoped docs, profiles,\n * change requests + RAG search) for the Agent API client.\n */\n\nimport { ApiBase, encodeFilePath, REQUEST_TIMEOUT_MS } from \"../transport.js\";\n\n/** Matches services/knowledge's maximum indexed document size. */\nexport const MAX_KNOWLEDGE_DOCUMENT_BYTES = 2 * 1024 * 1024;\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 + 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 /**\n * Provenance of the hit. All live results are `\"doc\"`; `\"fact\"` only ever\n * appears for legacy vectors indexed before the facts primitive was removed\n * (the search index stays tolerant of them). Treat every hit as a doc.\n */\n source: \"doc\" | \"fact\";\n /** The canonical file under shared/<scope>/ (present on doc hits). */\n filePath?: string;\n /** Legacy-only: the id of a pre-removal fact vector. */\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 type ChangeRequestResourceType = \"doc\" | \"profile\";\nexport type ChangeRequestOperation = \"create\" | \"update\" | \"delete\";\nexport type ChangeRequestStatus =\n | \"open\"\n | \"approved\"\n | \"rejected\"\n | \"withdrawn\"\n | \"superseded\";\nexport type ChangeRequestActorKind = \"human\" | \"agent\";\n\n/** Public projection of a change request (mirrors `PublicChangeRequest` in services/org). */\nexport interface KnowledgeChangeRequest {\n changeRequestId: string;\n scopeType: KnowledgeScopeType;\n scopeId: string;\n resourceType: ChangeRequestResourceType;\n operation: ChangeRequestOperation;\n targetPath: string | null;\n baseVersionId: string | null;\n proposedContentType: string | null;\n status: ChangeRequestStatus;\n proposerId: string;\n proposerKind: ChangeRequestActorKind;\n rationale: string;\n reviewerId: string | null;\n reviewerKind: ChangeRequestActorKind | null;\n reviewedAt: string | null;\n reviewNote: string | null;\n appliedRef: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/** Per-type proposal payload for `proposeScopeChange`. */\nexport interface ProposeScopeChangeInput {\n resourceType: ChangeRequestResourceType;\n operation: ChangeRequestOperation;\n /** Why the change is proposed — shown to the reviewer. */\n rationale: string;\n /** doc: the path the proposal applies to (e.g. designs/data-center.md). */\n targetPath?: string;\n /** doc create/update: the staged body to upload (markdown or other text). */\n content?: string;\n /** doc create/update: content type of the staged body (default text/markdown). */\n contentType?: string;\n /** profile: the proposed value ({ about, description, links }). */\n proposedValue?: unknown;\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// ─── Knowledge resources (org/team/project) ──────────────\n//\n// Scoped knowledge ABOUT a thing being worked on. Search hits\n// `services/knowledge` (RAG); docs + 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\nexport class KnowledgeApi extends ApiBase {\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.transport.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.transport.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.transport.request<KnowledgeProfile>(\n `/agent/org/profile/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n );\n }\n\n // Propose a change to a scope's knowledge instead of writing it directly.\n // Use this ONLY when the agent is not a member of the target scope (direct\n // resource_write_* is refused there) or otherwise cannot write directly —\n // the proposal is inert until a scope reviewer approves it. Create is gated\n // on a valid agent token only (no membership needed), which is what lets a\n // non-member contribute.\n\n /**\n * Open a change request against a scope's knowledge resource. For a doc\n * create/update, `services/org` returns a presigned staging PUT; this method\n * uploads the proposed `content` to it (echoing the same Content-Type that\n * was signed), mirroring `writeScopeDoc`. The staged body is applied to the\n * canonical doc — attributed to this agent — only when a reviewer approves.\n */\n async proposeScopeChange(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n input: ProposeScopeChangeInput,\n ): Promise<KnowledgeChangeRequest> {\n const isDocBody =\n input.resourceType === \"doc\" && input.operation !== \"delete\";\n const contentType = input.contentType ?? \"text/markdown\";\n\n const result = await this.transport.request<{\n changeRequest: KnowledgeChangeRequest;\n uploadUrl?: string;\n requiredHeaders?: Record<string, string>;\n }>(\n `/agent/org/change-requests/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n {\n method: \"POST\",\n body: JSON.stringify({\n resourceType: input.resourceType,\n operation: input.operation,\n rationale: input.rationale,\n targetPath: input.targetPath,\n proposedContentType: isDocBody ? contentType : undefined,\n proposedValue: input.proposedValue,\n }),\n },\n );\n\n // doc create/update: stage the proposed body at the returned presigned PUT.\n if (isDocBody && result.uploadUrl) {\n const putHeaders = new Headers(result.requiredHeaders ?? {});\n // The staging PUT signed `ContentType` into the URL — the PUT must echo\n // the exact same value or S3 rejects with SignatureDoesNotMatch.\n putHeaders.set(\"Content-Type\", contentType);\n const res = await fetch(result.uploadUrl, {\n method: \"PUT\",\n body: input.content ?? \"\",\n headers: putHeaders,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n await res.text();\n throw new Error(`Change-request body upload failed (${String(res.status)})`);\n }\n }\n\n return result.changeRequest;\n }\n\n /**\n * List the agent's OWN change requests in a scope (filtered server-side to\n * this agent as proposer). Pass `status` to narrow to open / approved / etc.\n */\n async listScopeChangeRequests(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n opts?: { status?: ChangeRequestStatus; limit?: number; cursor?: string },\n ): Promise<{ changeRequests: KnowledgeChangeRequest[]; nextCursor: string | null }> {\n const qs = new URLSearchParams();\n if (opts?.status) qs.set(\"status\", opts.status);\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.transport.request<{ changeRequests: KnowledgeChangeRequest[]; nextCursor: string | null }>(\n `/agent/org/change-requests/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}${query ? `?${query}` : \"\"}`,\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.transport.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 opts?: { maxBytes?: number },\n ): Promise<{ filePath: string; text: string }> {\n const maxBytes = opts?.maxBytes ?? MAX_KNOWLEDGE_DOCUMENT_BYTES;\n if (\n !Number.isInteger(maxBytes) ||\n maxBytes < 1 ||\n maxBytes > MAX_KNOWLEDGE_DOCUMENT_BYTES\n ) {\n throw new RangeError(\n `maxBytes must be an integer from 1 to ${String(MAX_KNOWLEDGE_DOCUMENT_BYTES)}`,\n );\n }\n const { downloadUrl } = await this.transport.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.body?.cancel().catch(() => undefined);\n throw new Error(`Doc download failed (${String(res.status)})`);\n }\n const text = await readBoundedUtf8(res, maxBytes);\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.transport.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\nasync function readBoundedUtf8(response: Response, maxBytes: number): Promise<string> {\n const declaredLength = response.headers.get(\"content-length\");\n if (declaredLength !== null && /^\\d+$/u.test(declaredLength) && Number(declaredLength) > maxBytes) {\n await response.body?.cancel().catch(() => undefined);\n throw documentTooLargeError(maxBytes);\n }\n if (response.body === null) return \"\";\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n let complete = false;\n try {\n while (!complete) {\n const { done, value } = await reader.read();\n if (done) {\n complete = true;\n continue;\n }\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined);\n throw documentTooLargeError(maxBytes);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n try {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n throw new Error(\"Knowledge document is not valid UTF-8 text\");\n }\n}\n\nfunction documentTooLargeError(maxBytes: number): Error & { code: string } {\n const error = new Error(\n `Knowledge document exceeds the ${String(maxBytes)} byte read limit`,\n ) as Error & { code: string };\n error.name = \"KnowledgeDocumentTooLargeError\";\n error.code = \"KNOWLEDGE_DOCUMENT_TOO_LARGE\";\n return error;\n}\n","/**\n * Cloud memory methods (Turbopuffer vectors + DynamoDB knowledge graph)\n * for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Memory ──────────────────────────────────────────────\n//\n// Cloud memory (Turbopuffer vectors + DynamoDB knowledge graph).\n// The agent API derives tenantId + agentId from the agent token.\n\nexport class MemoryApi extends ApiBase {\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.transport.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.transport.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 }, ingestEpoch?: number): Promise<{ queued: boolean; messageCount: number }> {\n return this.transport.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 // Per-boot monotonic epoch from live auto-capture. Lets the memory\n // service reset the per-session high-water mark after a daemon restart\n // (which resets the client's message-index counter) so post-restart\n // captures aren't silently dropped. Omitted by backfill, which uses\n // stable file-position indices. Only sent when provided.\n ...(ingestEpoch !== undefined ? { ingestEpoch } : {}),\n messages,\n metadata,\n }),\n });\n }\n\n async memoryLoadContext(tier?: number, topicHint?: string): Promise<{\n tier: number;\n facts: { subject: string; predicate: string; object: string; since: string }[];\n memories: { text: string; topic: string; subtopic: string; score: number }[];\n tokenEstimate: number;\n formatted: string;\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.transport.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.transport.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.transport.request(\"/agent/memory/navigate\");\n }\n\n async memoryDelete(memoryId: string): Promise<{ deleted: boolean }> {\n return this.transport.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.transport.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.transport.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<{\n synced: boolean;\n syncedAt?: string;\n sessionsBackfillSynced?: boolean;\n sessionsBackfillSyncedAt?: string;\n }> {\n return this.transport.request(\"/agent/memory/bootstrap-status\");\n }\n\n async memoryBootstrapStatusMark(\n scope?: \"files\" | \"sessions\",\n ): Promise<{ synced: true; syncedAt: string }> {\n return this.transport.request(\"/agent/memory/bootstrap-status\", {\n method: \"POST\",\n ...(scope ? { body: JSON.stringify({ scope }) } : {}),\n });\n }\n}\n","/**\n * Mobile (numbers / SMS / calls) + WhatsApp methods (services/mobile)\n * for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Mobile / WhatsApp types (services/mobile) ────────────\n\n/** Response of GET /mobile/numbers for an agent (services/mobile). */\nexport interface MobileNumberInfo {\n phoneNumber: string;\n countryCode: string;\n monthlyPrice?: number;\n status: string;\n errorMessage?: string;\n}\n\n/** One purchasable number from GET /mobile/numbers/search. */\nexport interface MobileAvailableNumber {\n number: string;\n friendlyName: string;\n locality: string;\n region: string;\n country: string;\n}\n\n/** Approved WhatsApp content template from GET /mobile/whatsapp/templates. */\nexport interface WhatsAppTemplate {\n contentSid: string;\n name: string;\n language: string;\n body: string;\n variables: Record<string, string>;\n category?: string;\n}\n\n// ─── Mobile (numbers / SMS / calls) ──────────────────────\n//\n// services/mobile routes. Dual-auth endpoints — with an agent\n// token the backend resolves agentId + tenantId from the token,\n// so no agentId is sent.\n\nexport class MobileApi extends ApiBase {\n async getMobileNumber(): Promise<MobileNumberInfo> {\n return this.transport.request(\"/mobile/numbers\");\n }\n\n async searchMobileNumbers(args?: {\n country?: string;\n query?: string;\n }): Promise<{ numbers: MobileAvailableNumber[]; monthlyPrice: number }> {\n const qs = new URLSearchParams();\n if (args?.country) qs.set(\"country\", args.country);\n if (args?.query) qs.set(\"query\", args.query);\n const query = qs.toString();\n return this.transport.request(`/mobile/numbers/search${query ? `?${query}` : \"\"}`);\n }\n\n async assignMobileNumber(args: {\n phoneNumber: string;\n countryCode: string;\n }): Promise<{ phoneNumber: string; countryCode: string; status: \"pending\" }> {\n return this.transport.request(\"/mobile/numbers/assign\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async releaseMobileNumber(): Promise<{ released: true }> {\n return this.transport.request(\"/mobile/numbers/release\", {\n method: \"POST\",\n body: JSON.stringify({}),\n });\n }\n\n async sendSms(args: { to: string; body: string }): Promise<{ sent: true; sid: string }> {\n return this.transport.request(\"/mobile/sms/send\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async startOutboundCall(args: { to: string }): Promise<{ callSid: string; status: string }> {\n return this.transport.request(\"/mobile/calls/outbound\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n // ─── WhatsApp ────────────────────────────────────────────\n\n async getWhatsAppSession(to: string): Promise<{ active: boolean; expiresAt?: string }> {\n return this.transport.request(`/mobile/whatsapp/session?to=${encodeURIComponent(to)}`);\n }\n\n async sendWhatsAppMessage(args: { to: string; body: string }): Promise<{ sent: true; sid: string }> {\n return this.transport.request(\"/mobile/whatsapp/send\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async sendWhatsAppTemplate(args: {\n to: string;\n contentSid: string;\n contentVariables: Record<string, string>;\n bodyPreview?: string;\n }): Promise<{ sent: true; sid: string }> {\n return this.transport.request(\"/mobile/whatsapp/send-template\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async listWhatsAppTemplates(): Promise<{ templates: WhatsAppTemplate[] }> {\n return this.transport.request(\"/mobile/whatsapp/templates\");\n }\n}\n","/**\n * Remote (interactive relay) methods — browser co-browse / terminal takeover\n * sessions brokered by the relay service. Ported from main's monolith\n * additions (b5e3c1e3) into the domain-split layout.\n */\nimport { ApiBase } from \"../transport.js\";\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\nexport class RemoteApi extends ApiBase {\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.transport.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.transport.request(`/agent/remote/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async completeRemoteSession(sessionId: string): Promise<{ ok: boolean }> {\n return this.transport.request(`/agent/remote/sessions/${encodeURIComponent(sessionId)}/complete`, {\n method: \"POST\",\n body: JSON.stringify({}),\n });\n }\n}\n","/**\n * Web/image/news search methods (services/search) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── News types (metered News MCP) ───────────────────────\n\n/**\n * The broad-news providers behind the metered `services/news` Lambda. The\n * server validates this with a zod enum; a value outside the union is an\n * unpriceable product, so keep the literal union in lockstep with the service.\n */\nexport type NewsProvider = \"apitube\" | \"newsdata\";\n\n/** One normalized article. `sentiment` is provider-shaped (APITube supplies it). */\nexport interface NewsArticle {\n title: string;\n url: string;\n source: string;\n publishedAt: string;\n snippet: string;\n sentiment?: unknown;\n}\n\n/** Provider-agnostic result — the server normalizes every adapter to this. */\nexport interface NewsResult {\n articles: NewsArticle[];\n provider: string;\n}\n\n// ─── Search ──────────────────────────────────────────────\n\nexport class SearchApi extends ApiBase {\n async searchWeb(params: {\n query: string;\n count?: number;\n offset?: number;\n country?: string;\n freshness?: string;\n }, options?: { signal?: AbortSignal }): Promise<unknown> {\n return this.transport.request(\"/agent/search/web\", {\n method: \"POST\",\n body: JSON.stringify(params),\n }, { signal: options?.signal });\n }\n\n async searchImages(params: {\n query: string;\n count?: number;\n }, options?: { signal?: AbortSignal }): Promise<unknown> {\n return this.transport.request(\"/agent/search/images\", {\n method: \"POST\",\n body: JSON.stringify(params),\n }, { signal: options?.signal });\n }\n\n async searchNews(params: {\n query: string;\n count?: number;\n offset?: number;\n freshness?: string;\n }, options?: { signal?: AbortSignal }): Promise<unknown> {\n return this.transport.request(\"/agent/search/news\", {\n method: \"POST\",\n body: JSON.stringify(params),\n }, { signal: options?.signal });\n }\n\n // ─── News (metered, provider-pluggable) ──────────────────\n //\n // Broad web-search-style news via the `services/news` money-path Lambda\n // (APITube / NewsData behind a `provider` arg). DISTINCT from `searchNews`\n // above (Brave, `/agent/search/news`) — these hit `/agent/news/...`. Provider\n // keys + metering live server-side; the client only forwards a typed body.\n\n /** Search news across the selected provider's corpus. → POST /agent/news/search */\n async newsSearch(params: {\n query: string;\n provider?: NewsProvider;\n source?: string;\n from?: string;\n to?: string;\n language?: string;\n category?: string;\n limit?: number;\n }): Promise<NewsResult> {\n return this.transport.request<NewsResult>(\"/agent/news/search\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n /** Top headlines for the selected provider. → POST /agent/news/headlines */\n async newsHeadlines(params?: {\n provider?: NewsProvider;\n category?: string;\n source?: string;\n language?: string;\n limit?: number;\n }): Promise<NewsResult> {\n return this.transport.request<NewsResult>(\"/agent/news/headlines\", {\n method: \"POST\",\n body: JSON.stringify(params ?? {}),\n });\n }\n}\n","/**\n * Per-scope secret store methods (envelope CRUD + KMS proxy) for the\n * Agent API client.\n */\n\nimport type {\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\";\nimport { ApiBase } from \"../transport.js\";\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\nexport class SecretsApi extends ApiBase {\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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.request<{ scopes: ScopeInfo[] }>(\"/agent/secrets/scopes\");\n return resp.scopes;\n }\n}\n","/**\n * Self-identity methods — the agent customizing its OWN identity (name,\n * avatar, voice). agentId + tenantId are resolved from the token, so no id\n * appears in the request. Ported from main's monolith additions into the\n * domain-split layout.\n */\nimport { ApiBase, isTransientRequestError, sleep } from \"../transport.js\";\n\n// Async avatar-generation job polling — same contract as image generation:\n// gen runs off-request so a multi-minute poll (in the daemon, not a Lambda)\n// avoids the API Gateway 30s ceiling.\nconst AVATAR_POLL_INTERVAL_MS = 2_000;\nconst AVATAR_JOB_TIMEOUT_MS = 180_000;\n\n// ─── Self identity types ─────────────────────────────────\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\nexport class SelfApi extends ApiBase {\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.transport.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 (same reason as `generateImage`): avatar gen runs `gpt-image-1`\n * (30–60s) which exceeds the API Gateway 30s ceiling, so this enqueues a job\n * (`POST /agent/avatar/generate` → `jobId`) then polls (`GET /agent/avatar/{jobId}`)\n * until the avatar is set. Signature unchanged — the plugin is unaffected.\n */\n async generateAvatar(args: { prompt: string }): Promise<AgentSelf> {\n // Enqueue via a NON-retrying POST — `scheduleJob` is not idempotent, so a\n // retried enqueue would double-run the metered job.\n const { jobId } = await this.transport.request<{ jobId: string }>(\n \"/agent/avatar/generate\",\n { method: \"POST\", body: JSON.stringify(args) },\n { retry: false },\n );\n\n const deadline = Date.now() + AVATAR_JOB_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await sleep(AVATAR_POLL_INTERVAL_MS);\n let job: {\n status: \"pending\" | \"running\" | \"completed\" | \"failed\";\n agent?: AgentSelf;\n error?: string;\n };\n try {\n job = await this.transport.request(`/agent/avatar/${jobId}`);\n } catch (error) {\n if (isTransientRequestError(error)) continue;\n throw error;\n }\n\n if (job.status === \"completed\") {\n if (!job.agent) throw new Error(\"Avatar generation completed without an agent\");\n return job.agent;\n }\n if (job.status === \"failed\") {\n const detail = job.error ? `: ${job.error.split(\"\\n\")[0]}` : \"\";\n throw new Error(`Avatar generation failed${detail}`);\n }\n // pending | running → keep polling\n }\n throw new Error(\"Avatar generation timed out\");\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.transport.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.transport.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.transport.request<{ voices: AgentVoice[] }>(\"/agent/voices\");\n }\n}\n","/**\n * Voice one-shot TTS / STT methods.\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 and go\n * through the transport's `rawRequest` instead. Metering to the tenant credit\n * pool happens server-side; the caller just gets audio (TTS) or a transcript\n * (STT). Ported from main's monolith additions into the domain-split layout.\n */\nimport { ApiBase } from \"../transport.js\";\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 VoiceApi extends ApiBase {\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.transport.rawRequest(\"/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.transport.rawRequest(\"/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","/**\n * Sync + shared (org/team/project) file methods for the Agent API client.\n */\n\nimport { ApiBase, encodeFilePath } from \"../transport.js\";\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// ─── 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\nexport class SyncApi extends ApiBase {\n async syncRegister(args?: { displayName?: string }): Promise<{ agent: SyncAgentInfo }> {\n return this.transport.request(\"/agent/sync/register\", {\n method: \"POST\",\n body: JSON.stringify(args ?? {}),\n });\n }\n\n async syncGetManifest(): Promise<SyncManifest> {\n return this.transport.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.transport.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.transport.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.transport.request(\"/agent/sync/reconstruct\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncGetStats(): Promise<SyncAgentStats> {\n return this.transport.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.transport.request(`/agent/sync/files${query ? `?${query}` : \"\"}`);\n }\n\n async syncListSessions(): Promise<{ sessions: SyncSessionEntry[] }> {\n return this.transport.request(\"/agent/sync/sessions\");\n }\n\n async syncGetSession(sessionId: string): Promise<SyncSessionContent> {\n return this.transport.request(`/agent/sync/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async syncDeleteFile(filePath: string): Promise<{ removed: boolean }> {\n return this.transport.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 limit?: number;\n cursor?: string;\n }): Promise<{ files: SharedFileEntry[]; nextCursor: string | null }> {\n const params = new URLSearchParams();\n if (args.limit !== undefined) params.set(\"limit\", String(args.limit));\n if (args.cursor) params.set(\"cursor\", args.cursor);\n const query = params.toString();\n return this.transport.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}${query ? `?${query}` : \"\"}`,\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.transport.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/download/${encodeFilePath(args.filePath)}`,\n );\n }\n}\n","/**\n * Microsoft Teams adapter methods (services/microsoft bot credentials +\n * messaging) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\nexport class TeamsApi extends ApiBase {\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.transport.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.transport.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.transport.request(\"/agent/microsoft/channels\");\n }\n}\n","/**\n * Workspace + template file methods for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Workspace types ─────────────────────────────────────\n\n/** Response of GET /agent/workspace (services/agents). */\nexport interface AgentWorkspaceInfo {\n templateKey?: string;\n defaultModel?: string;\n installedFrom?: { templateKey: string; authorTenantId: string; version: number };\n runtime?: string;\n teams?: { teamId: string; name: string; description?: string; parentTeamId?: string }[];\n projects?: { projectId: string; name: string; description?: string; status: string; parentProjectId?: string }[];\n teamIds?: string[];\n projectIds?: string[];\n}\n\nexport class WorkspaceApi extends ApiBase {\n /**\n * GET /agent/workspace — workspace config for the authenticated agent\n * (template assignment, default model, org roster).\n */\n async getWorkspace(): Promise<AgentWorkspaceInfo> {\n return this.transport.request<AgentWorkspaceInfo>(\"/agent/workspace\");\n }\n\n /**\n * GET /templates/{key}/files — persona/workspace file contents for a\n * template the agent has access to. Pass `version` to pin to the version\n * the agent was installed from (omit → the endpoint resolves `latest`).\n */\n async getTemplateFiles(\n templateKey: string,\n opts?: { version?: number },\n ): Promise<{ files: Record<string, string> }> {\n const query = opts?.version !== undefined ? `?version=${String(opts.version)}` : \"\";\n return this.transport.request<{ files: Record<string, string> }>(\n `/agent/templates/${encodeURIComponent(templateKey)}/files${query}`,\n );\n }\n}\n","/** Agent self-service webhook management methods. */\nimport { ApiBase } from \"../transport.js\";\n\nexport interface AgentWebhook {\n webhookId: string;\n tenantId: string;\n agentId: string;\n name: string;\n provider: string;\n active: boolean;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreatedAgentWebhook extends AgentWebhook {\n url: string;\n signingSecret: string;\n}\n\nexport interface AgentWebhookDelivery {\n deliveryId: string;\n webhookId: string;\n status: string;\n attempts: number;\n createdAt: string;\n deliveredAt?: string;\n}\n\nexport class WebhooksApi extends ApiBase {\n async createWebhook(args: {\n name: string;\n provider?: \"generic\" | \"github\" | \"stripe\" | \"slack\";\n }): Promise<CreatedAgentWebhook> {\n return this.transport.request<CreatedAgentWebhook>(\"/agent/webhooks\", {\n method: \"POST\",\n body: JSON.stringify(args),\n }, { retry: false });\n }\n\n async listWebhooks(): Promise<AgentWebhook[]> {\n const result = await this.transport.request<{ webhooks: AgentWebhook[] }>(\"/agent/webhooks\");\n return result.webhooks;\n }\n\n async deleteWebhook(webhookId: string): Promise<{ webhookId: string; active: false }> {\n return this.transport.request(`/agent/webhooks/${encodeURIComponent(webhookId)}`, {\n method: \"DELETE\",\n });\n }\n\n async rotateWebhookSecret(webhookId: string): Promise<{ webhookId: string; signingSecret: string }> {\n return this.transport.request(\n `/agent/webhooks/${encodeURIComponent(webhookId)}/rotate`,\n { method: \"POST\" },\n { retry: false },\n );\n }\n\n async listWebhookDeliveries(webhookId: string): Promise<AgentWebhookDelivery[]> {\n const result = await this.transport.request<{ deliveries: AgentWebhookDelivery[] }>(\n `/agent/webhooks/${encodeURIComponent(webhookId)}/deliveries`,\n );\n return result.deliveries;\n }\n}\n","/**\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 {\n installToolErrorCapture,\n type ToolCaptureApi,\n type InstallToolErrorCaptureOptions,\n} from \"./tool-error-capture.js\";\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\nexport type { AgentApiClientConfig } from \"./transport.js\";\nexport type { AgentWorkspaceInfo } from \"./domains/workspace.js\";\nexport type {\n SyncAgentInfo,\n SyncManifestEntry,\n SyncManifest,\n SyncPresignedUrl,\n SyncConfirmedUpload,\n SyncReconstructFile,\n SyncReconstructBundle,\n SyncAgentStats,\n SyncFileEntry,\n SyncSessionEntry,\n SyncSessionContent,\n SharedFileEntry,\n} from \"./domains/sync.js\";\nexport type {\n KnowledgeScopeType,\n KnowledgeScope,\n KnowledgeSearchHit,\n KnowledgeSearchResult,\n KnowledgeProfileLink,\n KnowledgeProfile,\n KnowledgeDoc,\n ChangeRequestResourceType,\n ChangeRequestOperation,\n ChangeRequestStatus,\n ChangeRequestActorKind,\n KnowledgeChangeRequest,\n ProposeScopeChangeInput,\n} from \"./domains/knowledge.js\";\nexport type {\n MobileNumberInfo,\n MobileAvailableNumber,\n WhatsAppTemplate,\n} from \"./domains/mobile.js\";\nexport type { RemoteSessionInfo } from \"./domains/remote.js\";\nexport type {\n AgentVoiceConfig,\n AgentSelf,\n AgentAvatarPresign,\n AgentVoice,\n} from \"./domains/self.js\";\nexport type {\n VoiceTtsModel,\n VoiceTtsArgs,\n VoiceTtsResult,\n VoiceSttArgs,\n VoiceSttResult,\n} from \"./domains/voice.js\";\nexport type {\n NewsProvider,\n NewsArticle,\n NewsResult,\n} from \"./domains/search.js\";\nexport type {\n AgentWebhook,\n CreatedAgentWebhook,\n AgentWebhookDelivery,\n} from \"./domains/webhooks.js\";\n\nimport { AgentApiTransport, ApiBase, type AgentApiClientConfig } from \"./transport.js\";\nimport { ChatApi } from \"./domains/chat.js\";\nimport { ConnectCredentialsApi } from \"./domains/connect-credentials.js\";\nimport { DatabaseApi } from \"./domains/database.js\";\nimport { IdentityApi } from \"./domains/identity.js\";\nimport { ImagesApi } from \"./domains/images.js\";\nimport { IntegrationsApi } from \"./domains/integrations.js\";\nimport { KnowledgeApi } from \"./domains/knowledge.js\";\nimport { MemoryApi } from \"./domains/memory.js\";\nimport { MobileApi } from \"./domains/mobile.js\";\nimport { RemoteApi } from \"./domains/remote.js\";\nimport { SearchApi } from \"./domains/search.js\";\nimport { SecretsApi } from \"./domains/secrets.js\";\nimport { SelfApi } from \"./domains/self.js\";\nimport { VoiceApi } from \"./domains/voice.js\";\nimport { SyncApi } from \"./domains/sync.js\";\nimport { TeamsApi } from \"./domains/teams.js\";\nimport { WorkspaceApi } from \"./domains/workspace.js\";\nimport { WebhooksApi } from \"./domains/webhooks.js\";\n\n// The client is assembled from per-domain method groups (each an ApiBase\n// subclass under ./domains/). Declaration merging presents the union as one\n// flat class type — the public surface is unchanged from the pre-split\n// single-class layout — while applyMixins() copies the prototype methods\n// onto AgentApiClient at module load.\n// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -- mixin assembly; applyMixins() below supplies every merged member\nexport interface AgentApiClient\n extends SyncApi,\n IntegrationsApi,\n WorkspaceApi,\n ConnectCredentialsApi,\n TeamsApi,\n ChatApi,\n SecretsApi,\n IdentityApi,\n MemoryApi,\n SearchApi,\n KnowledgeApi,\n DatabaseApi,\n MobileApi,\n RemoteApi,\n SelfApi,\n VoiceApi,\n ImagesApi,\n WebhooksApi {}\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -- see interface note above\nexport class AgentApiClient extends ApiBase {\n constructor(config: AgentApiClientConfig) {\n super(new AgentApiTransport(config));\n }\n}\n\n/** Copy each domain group's prototype methods onto the client class. */\nfunction applyMixins(derived: { prototype: object }, bases: { prototype: object }[]): void {\n for (const base of bases) {\n for (const name of Object.getOwnPropertyNames(base.prototype)) {\n if (name === \"constructor\") continue;\n const descriptor = Object.getOwnPropertyDescriptor(base.prototype, name);\n if (descriptor) Object.defineProperty(derived.prototype, name, descriptor);\n }\n }\n}\n\napplyMixins(AgentApiClient, [\n SyncApi,\n IntegrationsApi,\n WorkspaceApi,\n ConnectCredentialsApi,\n TeamsApi,\n ChatApi,\n SecretsApi,\n IdentityApi,\n MemoryApi,\n SearchApi,\n KnowledgeApi,\n DatabaseApi,\n MobileApi,\n RemoteApi,\n SelfApi,\n VoiceApi,\n ImagesApi,\n WebhooksApi,\n]);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/tool-error-capture.ts","../src/transport.ts","../src/domains/chat.ts","../src/domains/connect-credentials.ts","../src/domains/database.ts","../src/domains/identity.ts","../src/domains/images.ts","../src/domains/integrations.ts","../src/domains/knowledge.ts","../src/domains/memory.ts","../src/domains/mobile.ts","../src/domains/remote.ts","../src/domains/search.ts","../src/domains/secrets.ts","../src/domains/self.ts","../src/domains/voice.ts","../src/domains/sync.ts","../src/domains/teams.ts","../src/domains/workspace.ts","../src/domains/webhooks.ts","../src/index.ts"],"sourcesContent":["/**\n * Tool-error capture for Alfe OpenClaw plugins.\n *\n * OpenClaw converts a thrown tool handler into a model-facing `tool_result`\n * WITHOUT logging, and most Alfe plugins catch-and-return an error result the\n * same silent way — so tool failures never appear in the runtime's output and\n * therefore never reach Sentry (the gateway daemon supervises the OpenClaw\n * process and reports error-looking output lines to the `agent-runtime`\n * project — see packages/gateway/src/runtime-output-monitor.ts).\n *\n * `installToolErrorCapture(api, { plugin })` closes that gap at the ONE choke\n * point every plugin already has: it wraps `api.registerTool` so every tool's\n * `execute` emits a deterministic, detector-matched line on failure:\n *\n * [ERROR] alfe-tool plugin=<plugin> tool=<name> <thrown|result-error>: <msg> (at <first-frame>)\n *\n * The `[ERROR]` prefix at line start is exactly what the daemon's\n * `ErrorLineDetector` classifies as an error-log block, so the failure lands\n * in Sentry fingerprinted by its normalized message — no Sentry SDK inside\n * the plugin process, no new dependency. Behavior toward OpenClaw and the\n * model is UNCHANGED: throws are rethrown, results returned as-is.\n */\n\n/**\n * Minimal shape of the OpenClaw plugin api this helper relies on. Method\n * syntax on purpose — TS checks method signatures bivariantly, so each\n * plugin's own concretely-typed `registerTool(tool: ToolDef): void` is\n * accepted without casts.\n */\nexport interface ToolCaptureApi {\n registerTool(...args: never[]): unknown;\n}\n\nexport interface InstallToolErrorCaptureOptions {\n /** Plugin package short-name for attribution (e.g. \"openclaw-secrets\"). */\n plugin: string;\n /**\n * Line sink — defaults to writing `process.stderr` directly (the plugin\n * runs in-process in OpenClaw, so this lands on the runtime's stderr, which\n * the daemon supervises — and a console patch can't reformat it away).\n * Injectable for tests.\n */\n emit?: (line: string) => void;\n}\n\ninterface ToolLike {\n name?: unknown;\n execute?: unknown;\n}\n\n// `Symbol.for` so the markers survive duplicate module copies (dual ESM/CJS\n// builds, multiple plugins bundling their own helper copy) — installing twice\n// or re-registering a module-level tool singleton must not stack wrappers.\nconst INSTALLED_MARKER = Symbol.for('alfe.toolErrorCapture.installed');\nconst WRAPPED_MARKER = Symbol.for('alfe.toolErrorCapture.wrapped');\n\n/** `isError: true` (MCP/Anthropic convention) or `details.status: 'error'` (OpenClaw). */\nfunction readResultErrorMessage(result: unknown): string | null {\n if (typeof result !== 'object' || result === null) return null;\n const r = result as {\n isError?: unknown;\n details?: { status?: unknown; error?: unknown };\n content?: { type?: unknown; text?: unknown }[];\n };\n const flagged = r.isError === true || r.details?.status === 'error';\n if (!flagged) return null;\n if (typeof r.details?.error === 'string') return r.details.error;\n if (Array.isArray(r.content)) {\n for (const item of r.content) {\n if (item.type === 'text' && typeof item.text === 'string') return item.text;\n }\n }\n return '(no error text)';\n}\n\n/** First stack frame, for inline context without emitting a multi-line block. */\nfunction firstFrame(err: unknown): string {\n if (!(err instanceof Error) || !err.stack) return '';\n const frame = err.stack.split('\\n').find((l) => l.trimStart().startsWith('at '));\n return frame ? ` (${frame.trim()})` : '';\n}\n\nfunction buildLine(\n plugin: string,\n tool: string,\n kind: 'thrown' | 'result-error',\n message: string,\n frame = '',\n): string {\n // Single line, [ERROR]-prefixed at position 0 — matched by the gateway\n // detector's LOG_ERROR_LEVEL as a one-line error-log block. Keep under the\n // detector's 500-char line cap.\n const safeToken = (value: string) => value.replace(/[^A-Za-z0-9_.@/-]+/g, '_').slice(0, 80);\n const stripControls = (value: string) =>\n Array.from(value, (character) => {\n const code = character.charCodeAt(0);\n return code < 32 || (code >= 127 && code <= 159) ? ' ' : character;\n }).join('');\n const oneLine = stripControls(message).replace(/\\s+/g, ' ').trim();\n const safeFrame = stripControls(frame).replace(/\\s+/g, ' ');\n return `[ERROR] alfe-tool plugin=${safeToken(plugin)} tool=${safeToken(tool)} ${kind}: ${oneLine}${safeFrame}`.slice(0, 480);\n}\n\nfunction wrapExecute(\n tool: ToolLike,\n opts: Required<Pick<InstallToolErrorCaptureOptions, 'plugin' | 'emit'>>,\n): void {\n const execute = tool.execute;\n if (typeof execute !== 'function') return;\n // Idempotent: plugins that re-register module-level tool singletons per\n // session would otherwise accrue a wrapper layer (K emissions per failure).\n const marked = tool as ToolLike & { [WRAPPED_MARKER]?: boolean };\n if (marked[WRAPPED_MARKER]) return;\n marked[WRAPPED_MARKER] = true;\n const name = typeof tool.name === 'string' ? tool.name : '(unnamed)';\n tool.execute = async (...args: unknown[]) => {\n try {\n const result: unknown = await (execute as (...a: unknown[]) => unknown).apply(tool, args);\n const resultError = readResultErrorMessage(result);\n if (resultError !== null) {\n try {\n opts.emit(buildLine(opts.plugin, name, 'result-error', resultError));\n } catch {\n /* capture must never affect the tool result */\n }\n }\n return result;\n } catch (err) {\n try {\n const message = err instanceof Error ? err.message : String(err);\n opts.emit(buildLine(opts.plugin, name, 'thrown', message, firstFrame(err)));\n } catch {\n /* capture must never mask the original error */\n }\n throw err;\n }\n };\n}\n\n/**\n * Wrap `api.registerTool` so every tool registered AFTER this call gets\n * failure capture. Handles both OpenClaw registration signatures:\n * `registerTool(toolDef)` and `registerTool((ctx) => toolDef, opts)`.\n * Call once, first thing in the plugin's `activate`/`register` entry.\n * Never throws.\n */\nexport function installToolErrorCapture(\n api: ToolCaptureApi,\n options: InstallToolErrorCaptureOptions,\n): void {\n try {\n const markedApi = api as ToolCaptureApi & { [INSTALLED_MARKER]?: boolean };\n if (markedApi[INSTALLED_MARKER]) return;\n // Write the supervised fd directly — console.error survives stream\n // re-routing but not a console patch that reformats lines (a JSON level\n // field or long prefix would break the detector's [ERROR]-prefix match).\n const emit =\n options.emit ?? ((line: string) => { process.stderr.write(`${line}\\n`); });\n const opts = { plugin: options.plugin, emit };\n const original = api.registerTool.bind(api) as (...args: unknown[]) => unknown;\n const wrappedRegisterTool = (...args: unknown[]) => {\n let preparedArgs = args;\n try {\n const [first, ...rest] = args;\n if (typeof first === 'function') {\n // Factory signature: wrap the tool the factory produces.\n const factory = first as (...fa: unknown[]) => unknown;\n const wrappedFactory = (...fa: unknown[]) => {\n const tool = factory(...fa);\n if (typeof tool === 'object' && tool !== null) {\n try {\n wrapExecute(tool as ToolLike, opts);\n } catch {\n /* register the original tool if it is immutable */\n }\n }\n return tool;\n };\n preparedArgs = [wrappedFactory, ...rest];\n }\n if (typeof first === 'object' && first !== null) {\n wrapExecute(first as ToolLike, opts);\n }\n } catch {\n // Wrapping failed for this call — register the original arguments.\n preparedArgs = args;\n }\n // Registration itself is intentionally outside the best-effort wrapper\n // catch. If OpenClaw rejects a tool, calling registerTool a second time\n // can duplicate partial side effects and obscures the original failure.\n return original(...preparedArgs);\n };\n api.registerTool = wrappedRegisterTool;\n markedApi[INSTALLED_MARKER] = true;\n } catch {\n /* capture install must never break plugin activation */\n }\n}\n","/**\n * Shared HTTP transport for the Agent API client — request core, retry\n * policy, error formatting, and the `ApiBase` class the domain method\n * groups under `./domains/` build on.\n */\n\nexport interface AgentApiClientConfig {\n apiKey: string;\n apiUrl: 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 */\nexport function encodeFilePath(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\n/**\n * Build the thrown Error message for a non-2xx response. The\n * @alfe/api-core error envelope carries the server's detail as\n * `{ message }` (Zod failures add `{ issues }`); some handlers use\n * `{ error }`. Surfacing that detail matters for tool-facing callers —\n * e.g. GET /mobile/numbers 404s with \"No phone number assigned…\n * use mobile_search_numbers\", which guides the agent's next tool call.\n */\nfunction formatErrorMessage(status: number, rawBody: string): string {\n const prefix = `Agent API request failed (${String(status)})`;\n try {\n const json = JSON.parse(rawBody) as Record<string, unknown>;\n const issues = json.issues as { path?: string[]; message?: string }[] | undefined;\n if (Array.isArray(issues) && issues.length > 0) {\n const details = issues\n .map((i) => `${i.path?.join(\".\") ?? \"input\"}: ${i.message ?? \"invalid\"}`)\n .join(\"; \");\n return `${prefix}: validation failed — ${details}`;\n }\n const detail = typeof json.error === \"string\"\n ? json.error\n : typeof json.message === \"string\"\n ? json.message\n : undefined;\n if (detail) return `${prefix}: ${detail}`;\n } catch {\n // Non-JSON error body — fall back to the bare status message.\n }\n return prefix;\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).\nexport const 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 isSafeRetryMethod(method: string | undefined): boolean {\n const normalized = (method ?? \"GET\").toUpperCase();\n return normalized === \"GET\" || normalized === \"HEAD\" || normalized === \"OPTIONS\";\n}\n\nexport function 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/** Whether a completed request may be retried/polled without masking a real client error. */\nexport function isTransientRequestError(err: unknown): boolean {\n if (isRetryableNetworkError(err)) return true;\n const status = (err as { status?: unknown } | null)?.status;\n return typeof status === \"number\" && RETRYABLE_STATUS.has(status);\n}\n\nexport class AgentApiTransport {\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 /**\n * Binary sibling 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 (voice TTS/STT), so those go\n * through this instead. Auth (Bearer), the request budget, and the single\n * retry policy on transient 5xx / network errors is kept in sync with\n * `request()`. Safe read methods retry once by default; mutation methods do\n * not, because a response can be lost after a handler or provider call has\n * already succeeded.\n */\n async rawRequest(\n path: string,\n init: { method: string; headers: Headers; body?: BodyInit | Uint8Array },\n extra?: { retry?: boolean },\n ): Promise<Response> {\n const url = `${this.apiUrl}${path}`;\n init.headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n\n const retry = extra?.retry ?? isSafeRetryMethod(init.method);\n const maxAttempts = retry ? 2 : 1;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; 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 the cast.\n body: init.body as BodyInit | undefined,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n const errorBody = await res.text(); // drain body\n const error = new Error(formatErrorMessage(res.status, errorBody)) as Error & {\n status?: number;\n };\n error.status = res.status;\n if (attempt < maxAttempts && 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 < maxAttempts && 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 * @param extra.timeoutMs Per-request abort timeout (default REQUEST_TIMEOUT_MS).\n * Long endpoints (image generation) pass a larger value so the gateway's\n * own timeout wins with a readable status instead of a client-side abort.\n * @param extra.retry Whether to retry once on transient failures. Safe reads\n * (GET/HEAD/OPTIONS) default to true; mutations default to false. Set true\n * only when the endpoint's server-side contract is explicitly idempotent.\n * @param extra.signal Optional caller cancellation combined with the client's\n * own timeout budget. Aborting either signal cancels the request.\n */\n async request<T>(\n path: string,\n options?: RequestInit,\n extra?: { timeoutMs?: number; retry?: boolean; signal?: AbortSignal },\n ): 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 const timeoutMs = extra?.timeoutMs ?? REQUEST_TIMEOUT_MS;\n const retry = extra?.retry ?? isSafeRetryMethod(options?.method);\n const maxAttempts = retry ? 2 : 1;\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 // A 500 may also come from the route after a side effect has committed, and\n // a network failure can happen after request bytes reached the server.\n // Mutation methods therefore opt out unless a caller explicitly asserts a\n // server-side idempotency contract (the Connect token-refresh methods do).\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const res = await fetch(url, {\n ...options,\n headers,\n signal: extra?.signal\n ? AbortSignal.any([extra.signal, AbortSignal.timeout(timeoutMs)])\n : AbortSignal.timeout(timeoutMs),\n });\n\n if (!res.ok) {\n const errorBody = await res.text(); // drain body\n const error = new Error(formatErrorMessage(res.status, errorBody)) as Error & {\n status?: number;\n };\n error.status = res.status;\n if (attempt < maxAttempts && 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 < maxAttempts && 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/**\n * Base class for the domain method groups. Holds the shared transport;\n * `AgentApiClient` assembles the groups onto one class via `applyMixins`\n * (prototype copy), so methods keep their original `this`-on-the-client\n * call shape.\n */\nexport class ApiBase {\n protected readonly transport: AgentApiTransport;\n\n constructor(transport: AgentApiTransport) {\n this.transport = transport;\n }\n}\n","/**\n * Chat attachment + activity methods for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\nexport class ChatApi extends ApiBase {\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.transport.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.transport.request<{ recorded: boolean }>(\"/agent/activity\", {\n method: \"POST\",\n body: JSON.stringify(data),\n });\n }\n}\n","/**\n * services/connect credential + account methods (Google, GitHub, Xero,\n * Notion, Atlassian, MYOB, Salesforce, Microsoft 365) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\nexport class ConnectCredentialsApi extends ApiBase {\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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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 * `xeroTenantId` is the model-facing organisation selector. The separate\n * `accountIdentifier` is the Connect persistence key used for refresh and\n * may be an email; never substitute one for the other.\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.transport.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 // accountIdentifier may be the user's email when one OAuth grant\n // exposes multiple Xero organisations. It is not a tenant selector.\n // Preserve the absence so Pattern A consumers fail closed instead of\n // presenting an email as authority for an arbitrary first tenant.\n xeroTenantId: a.xeroTenantId ?? \"\",\n })),\n };\n }\n\n async refreshXeroToken(): Promise<{\n accessToken: string;\n expiresAt: string;\n }> {\n return this.transport.request(\n \"/agent/connect/xero/refresh\",\n { method: \"POST\" },\n { retry: true },\n );\n }\n\n /**\n * Refresh a specific Xero Connection by its exact `accountIdentifier` from\n * `getXeroAccounts()`. Do not substitute `xeroTenantId`: current Xero OAuth\n * rows may use the account email as their persistence key even when a sole\n * organisation tenant ID is available in provider metadata.\n */\n async refreshXeroAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/xero/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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.transport.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.transport.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.transport.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.transport.request(\n \"/agent/connect/atlassian/refresh\",\n { method: \"POST\" },\n { retry: true },\n );\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.transport.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.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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.transport.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.transport.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.transport.request(\n \"/agent/connect/myob/refresh\",\n { method: \"POST\" },\n { retry: true },\n );\n }\n\n /**\n * Pattern A: refresh one MYOB Connection by its stable\n * `accountIdentifier` (the MYOB business id returned by\n * `getMYOBAccounts()`).\n *\n * MYOB refresh tokens belong to individual Connection rows. A\n * multi-business client must use this method instead of refreshing the\n * primary Connection and copying that access token into every cached\n * business client.\n */\n async refreshMYOBAccountToken(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/myob/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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. 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.transport.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.transport.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.transport.request<{\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\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 email: string;\n microsoftTenantId: string;\n workspaceDomain: string;\n }[];\n }> {\n const raw = await this.transport.request<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken?: string;\n accessTokenExpiresAt?: 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 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.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\n return {\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n\n /**\n * Disconnects one connected Microsoft 365 account for the agent, by its\n * `accountIdentifier`. Hits the generic per-account disconnect route\n * (`DELETE /agent/connect/microsoft/accounts/{accountIdentifier}`), which\n * resolves across the agent's full effective scope chain and deletes the\n * matching Connection row. Returns the remaining accounts.\n *\n * IMPORTANT: pass the `accountIdentifier` from `getMicrosoftAccounts()`, NOT\n * a synthesised email. For Microsoft, `accountIdentifier` is the user's email\n * only when the Graph profile fetch succeeded at connect time; it falls back\n * to the Azure tenant id (`tid` claim) otherwise. The backend matches on\n * `accountIdentifier` exactly, so passing an email would 404 on those\n * fallback-identifier accounts. (This is why the param is not named `email`,\n * unlike `disconnectGoogleAccount` where the identifier is always the email.)\n */\n async disconnectMicrosoftAccount(accountIdentifier: string): Promise<{\n accounts: { accountIdentifier: string; displayName?: string; connectedAt?: string }[];\n }> {\n const raw = await this.transport.request<{\n accounts: { accountIdentifier: string; displayName?: string | null; connectedAt?: string }[];\n }>(`/agent/connect/microsoft/accounts/${encodeURIComponent(accountIdentifier)}`, {\n method: \"DELETE\",\n });\n return {\n accounts: raw.accounts.map((a) => ({\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName ?? undefined,\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n /**\n * Resolve the primary cTrader Connection's credentials for the calling\n * agent. Unlike most providers, the cTrader Open API needs app-level auth\n * (`clientId` + `clientSecret`) AND account auth (`accessToken` +\n * `accountId`) on the socket, so `@alfe.ai/ctrader-mcp` self-fetches the\n * full set here at startup (the atlassian/google pattern). `clientId` /\n * `clientSecret` are the SST-sourced global app credentials the connect\n * endpoint injects — they are never persisted on the connection. `host` is\n * the resolved TLS endpoint (`live.ctraderapi.com` / `demo.ctraderapi.com`)\n * derived from the selected account's live/demo flag.\n */\n async getCTraderCredentials(): Promise<{\n accessToken: string;\n refreshToken: string;\n accountId: string;\n host: string;\n clientId: string;\n clientSecret: string;\n }> {\n const raw = await this.transport.request<{\n accessToken?: string;\n refreshToken?: string;\n accountId?: string | number;\n host?: string;\n clientId?: string;\n clientSecret?: string;\n }>(\"/agent/connect/ctrader/credentials\");\n return {\n accessToken: raw.accessToken ?? \"\",\n refreshToken: raw.refreshToken ?? \"\",\n accountId: raw.accountId != null ? String(raw.accountId) : \"\",\n host: raw.host ?? \"\",\n clientId: raw.clientId ?? \"\",\n clientSecret: raw.clientSecret ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for cTrader.\n *\n * Unlike atlassian/salesforce (one Connection row per account/site), a\n * cTrader is MULTI-grant per agent: an agent may connect several distinct\n * cTrader logins, each its own Connection row keyed on `accountIdentifier =\n * ctid:<userId>` (Phase 1). This aggregates the *trading accounts* across\n * ALL of those Connection rows — each row contributes its `availableAccounts`\n * flattened, and every account carries ITS OWN grant's `accessToken` (the\n * token that authenticates that account against the cTrader Open API). One\n * OAuth grant still covers all accounts under that single login on one shared\n * token; only the `ctidTraderAccountId` and the protobuf socket `host` (live\n * vs demo) differ within a grant. Across grants the tokens differ, so the\n * token is now PER-ACCOUNT rather than hoisted to the top level.\n *\n * `host` per account is derived from the account's `isLive` flag\n * (`live.ctraderapi.com` / `demo.ctraderapi.com`) — the same mapping the\n * connect provider applies server-side when an account is auto-selected.\n *\n * `clientId` / `clientSecret` are the SST-sourced GLOBAL app credentials the\n * connect endpoint injects — identical across every Connection row (one\n * cTrader app), never persisted on a connection. We take them from the first\n * row that carries them.\n *\n * Accounts are deduped on `ctidTraderAccountId` first-wins: Spotware ids are\n * globally unique across logins, so a duplicate can only appear if the same\n * account somehow surfaced under two grants — first-wins keeps it\n * deterministic.\n *\n * `accounts` may be empty (no cTrader Connection at all), in which case we\n * return empty creds rather than throwing.\n */\n async getCTraderAccounts(): Promise<{\n accounts: {\n ctidTraderAccountId: string;\n host: string;\n isLive: boolean;\n brokerName?: string;\n accountNumber?: string;\n accessToken: string;\n /**\n * The stable per-grant Connection key (`ctid:<userId>`) this account\n * belongs to. Every trading account under one cTrader login shares one\n * grant (one OAuth token), so this is the identifier the MCP server\n * passes to `refreshCTraderAccount()` to rotate the token for the whole\n * grant on a `CH_ACCESS_TOKEN_INVALID` expiry. Empty string when the\n * server did not supply one (legacy rows) — such an account can still\n * trade with its current token but cannot self-refresh.\n */\n accountIdentifier: string;\n }[];\n clientId: string;\n clientSecret: string;\n }> {\n const raw = await this.transport.request<{\n provider: string;\n accounts: {\n accessToken?: string;\n clientId?: string;\n clientSecret?: string;\n accountIdentifier?: string;\n availableAccounts?: {\n ctidTraderAccountId?: string | number;\n accountId?: string | number;\n isLive?: boolean;\n brokerName?: string;\n accountNumber?: string | number;\n }[];\n }[];\n }>(\"/agent/connect/ctrader/accounts\");\n\n // No cTrader Connection rows → return empty creds rather than throwing.\n // `raw.accounts` is `T[]`, so an explicit length guard is how we model the\n // empty case (no noUncheckedIndexedAccess).\n if (raw.accounts.length === 0) {\n return { accounts: [], clientId: \"\", clientSecret: \"\" };\n }\n\n // Global app credentials are identical across rows (one cTrader app). Take\n // them from the first row that supplies them; guard the empty case.\n let clientId = \"\";\n let clientSecret = \"\";\n for (const row of raw.accounts) {\n if (!clientId && row.clientId) clientId = row.clientId;\n if (!clientSecret && row.clientSecret) clientSecret = row.clientSecret;\n if (clientId && clientSecret) break;\n }\n\n // Aggregate across ALL grant rows. Each account carries the row's own\n // `accessToken`. Dedup on ctidTraderAccountId first-wins.\n const seen = new Set<string>();\n const accounts: {\n ctidTraderAccountId: string;\n host: string;\n isLive: boolean;\n brokerName?: string;\n accountNumber?: string;\n accessToken: string;\n accountIdentifier: string;\n }[] = [];\n for (const row of raw.accounts) {\n const rowToken = row.accessToken ?? \"\";\n // The grant key (`ctid:<userId>`) is shared by every account under this\n // Connection row; stamp it onto each flattened account so the consumer\n // can refresh the whole grant's token by its exact accountIdentifier.\n const rowAccountIdentifier = row.accountIdentifier ?? \"\";\n for (const a of row.availableAccounts ?? []) {\n const id =\n a.ctidTraderAccountId != null\n ? String(a.ctidTraderAccountId)\n : a.accountId != null\n ? String(a.accountId)\n : \"\";\n if (id.length === 0 || seen.has(id)) continue;\n seen.add(id);\n const isLive = a.isLive === true;\n accounts.push({\n ctidTraderAccountId: id,\n host: isLive ? \"live.ctraderapi.com\" : \"demo.ctraderapi.com\",\n isLive,\n ...(a.brokerName != null ? { brokerName: a.brokerName } : {}),\n ...(a.accountNumber != null\n ? { accountNumber: String(a.accountNumber) }\n : {}),\n accessToken: rowToken,\n accountIdentifier: rowAccountIdentifier,\n });\n }\n }\n\n return { accounts, clientId, clientSecret };\n }\n\n /**\n * Pattern A: refresh a specific cTrader grant by its stable\n * `accountIdentifier` (`ctid:<userId>` from `getCTraderAccounts()`).\n *\n * cTrader access tokens live ~30 days; the `getCTraderAccounts()` /\n * credentials reads serve the STORED token without refreshing, so refresh is\n * the consumer's job. `@alfe.ai/ctrader-mcp` calls this when the cTrader Open\n * API rejects an account-auth with `CH_ACCESS_TOKEN_INVALID`, then re-runs\n * the socket handshake with the returned `accessToken`.\n *\n * Refreshing one grant rotates the single OAuth token that covers EVERY\n * trading account under that login. cTrader's refresh token itself does not\n * expire but may rotate on refresh (`rotatesRefreshToken: true`); connect\n * persists the rotated refresh token server-side, so the caller only needs\n * the new `accessToken`. Mirrors `refreshXeroAccountToken`.\n */\n async refreshCTraderAccount(accountIdentifier: string): Promise<{\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/ctrader/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\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. Use\n * `getShopifyAccounts()` for the multi-account shape required by Pattern A\n * (`@alfe.ai/shopify-mcp` keys per-shop on the myshopify domain).\n */\n async getShopifyCredentials(): Promise<{\n accessToken: string;\n shopDomain: string;\n shopGid: string;\n shopName: string;\n apiVersion: string;\n }> {\n const raw = await this.transport.request<{\n accessToken: string;\n shopDomain?: string;\n shopGid?: string;\n shopName?: string;\n apiVersion?: string;\n }>(\"/agent/connect/shopify/credentials\");\n return {\n accessToken: raw.accessToken,\n shopDomain: raw.shopDomain ?? \"\",\n shopGid: raw.shopGid ?? \"\",\n shopName: raw.shopName ?? \"\",\n apiVersion: raw.apiVersion ?? \"\",\n };\n }\n\n /**\n * Pattern A: multi-account credential fetch for Shopify. Returns every\n * agent-scoped Shopify Connection. One OAuth grant maps to one store, so the\n * stable per-call selector is the store's myshopify domain (`shopDomain`),\n * NOT `accountIdentifier` — the connect provider keys `accountIdentifier` on\n * the immutable shop GID (falling back to the domain), so `shopDomain` is the\n * value the LLM passes and the plugin routes on.\n *\n * Each entry is shaped by the connect provider's `buildCredentialsResponse`:\n * `{ accessToken, shopDomain, shopGid, shopName, apiVersion }` — offline\n * Shopify tokens never expire, so there is NO token / expiry field and no\n * refresh method (unlike Salesforce). The GraphQL Admin API authenticates\n * purely on `X-Shopify-Access-Token`; no client credentials are on the wire.\n */\n async getShopifyAccounts(): Promise<{\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n shopDomain: string;\n shopGid: string;\n shopName: string;\n apiVersion: string;\n }[];\n }> {\n const raw = await this.transport.request<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n connectedAt: string;\n accessToken: string;\n shopDomain?: string;\n shopGid?: string;\n shopName?: string;\n apiVersion?: string;\n }[];\n }>(\"/agent/connect/shopify/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 // `shopDomain` is the myshopify host the GraphQL/token requests target.\n // Fall back to `accountIdentifier` only when it already IS the domain\n // (the connect provider uses the domain as the identifier when the\n // shop-info fetch couldn't resolve a GID).\n shopDomain: a.shopDomain ?? \"\",\n shopGid: a.shopGid ?? \"\",\n shopName: a.shopName ?? \"\",\n apiVersion: a.apiVersion ?? \"\",\n })),\n };\n }\n\n /**\n * Pattern A: provider-parameterized multi-account credential fetch for the\n * social connectors (Bluesky, and the approval-gated backlog: X, Meta,\n * Threads, LinkedIn, Pinterest, TikTok, Reddit, YouTube).\n *\n * Unlike the bespoke `getGithubAccounts()` / `getXeroAccounts()` shapes,\n * this returns a UNIFORM normalized account shape so `@alfe.ai/social-mcp`'s\n * shared driver can require a single `account` selector on every\n * credential-touching tool regardless of platform. The backend\n * `api-agents/{provider}/accounts` route is already provider-generic; this\n * is the client-side normalization the plan (`do-we-need-any-moonlit-toucan`\n * Phase 0, step 5) calls for.\n *\n * `accountIdentifier` is the stable per-account selector the LLM should\n * pass back (for Bluesky: the account DID). `accessToken` carries whatever\n * the provider's `buildCredentialsResponse` bundles (for Bluesky: the JSON\n * session bundle — the driver parses the `accessJwt` out of it, or reads the\n * top-level `accessJwt` from `providerMetadata`-adjacent fields). Everything\n * else the driver needs for routing (handle, pdsHost, did, …) is on\n * `providerMetadata`.\n *\n * Token refresh is delegated to connect (never done in-plugin) via the\n * per-account route `POST /agent/connect/{provider}/accounts/{accountIdentifier}/refresh`\n * — call `refreshSocialAccount(provider, accountIdentifier)`. (The non-account\n * `POST /agent/connect/{provider}/refresh` route refreshes the provider's\n * PRIMARY connection, which is wrong under multi-account Pattern A.)\n */\n async getSocialAccounts(provider: string): Promise<{\n provider: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n accessToken: string;\n providerMetadata: Record<string, unknown>;\n connectedAt: string;\n }[];\n }> {\n const raw = await this.transport.request<{\n provider?: string;\n accounts: {\n connectionId: string;\n accountIdentifier: string;\n displayName: string | null;\n accessToken?: string;\n providerMetadata?: Record<string, unknown>;\n connectedAt: string;\n }[];\n }>(`/agent/connect/${encodeURIComponent(provider)}/accounts`);\n return {\n provider: raw.provider ?? provider,\n accounts: raw.accounts.map((a) => ({\n connectionId: a.connectionId,\n accountIdentifier: a.accountIdentifier,\n displayName: a.displayName,\n accessToken: a.accessToken ?? \"\",\n providerMetadata: a.providerMetadata ?? {},\n connectedAt: a.connectedAt,\n })),\n };\n }\n\n /**\n * Pattern A: refresh a specific social Connection by its stable\n * `accountIdentifier` (for Bluesky: the account DID) via the\n * provider-generic per-account refresh route. The counterpart to\n * `getSocialAccounts(provider)`; `@alfe.ai/social-mcp` calls this on a\n * 401/ExpiredToken from the platform PDS/API, then re-fetches accounts to\n * pick up the rotated bundle.\n *\n * Refresh itself is ALWAYS delegated to connect — the plugin never calls\n * the platform's own refresh XRPC (e.g. `com.atproto.server.refreshSession`)\n * because connect owns the encrypted refresh token + rotation persistence\n * (Bluesky rotates the refreshJwt; a missed rotation kills the connection\n * after one refresh). The returned `accessToken` is whatever the provider's\n * `refreshToken` hook re-bundled (for Bluesky: the JSON session bundle with\n * the fresh `accessJwt`) — callers typically ignore it and re-fetch via\n * `getSocialAccounts` for a consistent shape.\n */\n async refreshSocialAccount(\n provider: string,\n accountIdentifier: string,\n ): Promise<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt: string;\n expiresAt: string;\n }> {\n const path = `/agent/connect/${encodeURIComponent(provider)}/accounts/${encodeURIComponent(accountIdentifier)}/refresh`;\n const raw = await this.transport.request<{\n accountIdentifier: string;\n accessToken: string;\n accessTokenExpiresAt?: string;\n expiresAt?: string;\n }>(path, { method: \"POST\" }, { retry: true });\n return {\n accountIdentifier: raw.accountIdentifier,\n accessToken: raw.accessToken,\n accessTokenExpiresAt: raw.accessTokenExpiresAt ?? \"\",\n expiresAt: raw.expiresAt ?? \"\",\n };\n }\n}\n","/**\n * Per-tenant MongoDB methods (services/database) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Database ───────────────────────────────────────────\n\nexport class DatabaseApi extends ApiBase {\n async registerDatabaseCredentials(): Promise<{\n connectionString: string;\n username: string;\n password: string;\n databases: string[];\n }> {\n return this.transport.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.transport.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","/**\n * Identity resolution, verification, and CRM methods for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Identity ─────────────────────────────────────────────\n//\n// Identity resolution, permission enforcement, and CRM tools.\n// The agent API derives tenantId + agentId from the agent token.\n\nexport class IdentityApi extends ApiBase {\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.transport.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.transport.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.transport.request(`/agent/identity/search${query ? `?${query}` : \"\"}`);\n }\n\n async getIdentityContext(identityId: string): Promise<{\n context: unknown;\n }> {\n return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/context`);\n }\n\n async mergeIdentities(\n survivorId: string,\n args: { mergedId: string },\n ): Promise<{ ok: boolean; error?: string }> {\n return this.transport.request(`/agent/identity/${encodeURIComponent(survivorId)}/merge`, {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async unmergeIdentity(identityId: string): Promise<{ ok: boolean; error?: string }> {\n return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/unmerge`, {\n method: \"POST\",\n });\n }\n\n async addIdentityNote(identityId: string, args: {\n content: string;\n category?: string;\n }): Promise<{ noteId: string | null }> {\n return this.transport.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 }): Promise<{ ok: boolean }> {\n return this.transport.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 cursor?: string;\n }): Promise<{ entries: unknown[]; cursor: string | null }> {\n const qs = new URLSearchParams();\n if (args?.limit) qs.set(\"limit\", String(args.limit));\n if (args?.cursor) qs.set(\"cursor\", args.cursor);\n const query = qs.toString();\n return this.transport.request(`/agent/identity/${encodeURIComponent(identityId)}/changelog${query ? `?${query}` : \"\"}`);\n }\n\n async rollbackIdentity(identityId: string, args: {\n targetVersion: number;\n }): Promise<{ ok: boolean; entry?: unknown }> {\n return this.transport.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.transport.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\" | \"already_confirmed\";\n error?: string;\n }> {\n return this.transport.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.transport.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.transport.request(\"/agent/google/resolve-sender\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n}\n","/**\n * Image-generation method — text prompt → a stable, public CDN image URL.\n * Ported from main's monolith addition into the domain-split layout.\n */\nimport { ApiBase, isTransientRequestError, sleep } from \"../transport.js\";\n\n// Async image-generation job polling. Generation runs off-request on a job\n// worker (no API Gateway 30s ceiling), so the client enqueues then polls.\n// The plugin runs in the daemon (not a Lambda), so a multi-minute poll is fine.\nconst IMAGE_POLL_INTERVAL_MS = 2_000;\nconst IMAGE_JOB_TIMEOUT_MS = 180_000;\n\nexport class ImagesApi extends ApiBase {\n /**\n * Generate an image from a text prompt and get back a STABLE, public URL\n * (served from the agent-assets CDN — it does not expire). Embed the returned\n * `imageUrl` in a reply as markdown to show it to the user.\n *\n * ASYNC: `gpt-image-1` routinely runs 30–60s, which exceeds the API Gateway\n * 30s ceiling, so this enqueues a job (`POST /agent/images/generate` →\n * `jobId`) then polls (`GET /agent/images/{jobId}`) until it completes. The\n * worker's real failure message (e.g. an unsupported `size`) surfaces via the\n * job's `error` field.\n */\n async generateImage(args: {\n prompt: string;\n model?: string;\n size?: string;\n quality?: string;\n }): Promise<{ imageUrl: string; model: string }> {\n // Enqueue via a NON-retrying POST. `scheduleJob` mints a fresh job id per\n // call (not idempotent), so a retried enqueue could double-enqueue → two\n // metered generations. A lost enqueue response must fail clean, not retry.\n const { jobId } = await this.transport.request<{ jobId: string }>(\n \"/agent/images/generate\",\n { method: \"POST\", body: JSON.stringify(args) },\n { retry: false },\n );\n\n // Poll until the worker finishes. A transient poll error is TOLERATED — the\n // job keeps running (and bills) server-side, so a blip must not abandon it;\n // only a terminal `failed` or the deadline ends the wait.\n const deadline = Date.now() + IMAGE_JOB_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await sleep(IMAGE_POLL_INTERVAL_MS);\n let job: {\n status: \"pending\" | \"running\" | \"completed\" | \"failed\";\n imageUrl?: string;\n model?: string;\n error?: string;\n };\n try {\n job = await this.transport.request(`/agent/images/${jobId}`);\n } catch (error) {\n if (isTransientRequestError(error)) continue;\n throw error;\n }\n\n if (job.status === \"completed\") {\n if (!job.imageUrl) throw new Error(\"Image generation completed without a URL\");\n return { imageUrl: job.imageUrl, model: job.model ?? args.model ?? \"gpt-image-1\" };\n }\n if (job.status === \"failed\") {\n const detail = job.error ? `: ${job.error.split(\"\\n\")[0]}` : \"\";\n throw new Error(`Image generation failed${detail}`);\n }\n // pending | running → keep polling\n }\n throw new Error(\"Image generation timed out\");\n }\n}\n","/**\n * Integration lifecycle, OAuth, and registry methods for the Agent API client.\n */\n\nimport type {\n IntegrationInstall,\n IntegrationConfigResult,\n RegistryEntry,\n} from \"@alfe/types\";\nimport { ApiBase } from \"../transport.js\";\n\nexport class IntegrationsApi extends ApiBase {\n async listIntegrations(): Promise<IntegrationInstall[]> {\n return this.transport.request<IntegrationInstall[]>(\"/agent/integrations\");\n }\n\n async getIntegrationConfig(integrationId: string): Promise<IntegrationConfigResult> {\n try {\n return await this.transport.request<IntegrationConfigResult>(\n `/agent/integrations/${encodeURIComponent(integrationId)}/config`,\n );\n } catch (err) {\n // A 404 means the integration simply isn't installed for this agent —\n // an expected answer, not a failure. Return it as data so the LLM sees\n // `installed: false` instead of a surfaced tool error (Sentry RUNTIME-1).\n if ((err as { status?: number }).status === 404) {\n return { integrationId, config: {}, configSchema: [], installed: false };\n }\n throw err;\n }\n }\n\n async updateIntegrationConfig(\n integrationId: string,\n config: Record<string, unknown>,\n ): Promise<void> {\n await this.transport.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.transport.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.transport.request<IntegrationInstall>(\n `/agent/integrations/${encodeURIComponent(integrationId)}`,\n { method: \"DELETE\" },\n );\n }\n\n async getOAuthUrl(\n provider: string,\n scopes?: string[],\n options?: { shop?: 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 if (options?.shop) params.set(\"shop\", options.shop);\n return this.transport.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.transport.request(\n `/agent/integrations/oauth/status?provider=${encodeURIComponent(provider)}`,\n );\n }\n\n async getRegistry(): Promise<{ integrations: RegistryEntry[] }> {\n return this.transport.request<{ integrations: RegistryEntry[] }>(\"/integrations/registry\");\n }\n}\n","/**\n * Knowledge resource methods (org/team/project scoped docs, profiles,\n * change requests + RAG search) for the Agent API client.\n */\n\nimport { ApiBase, encodeFilePath, REQUEST_TIMEOUT_MS } from \"../transport.js\";\n\n/** Matches services/knowledge's maximum indexed document size. */\nexport const MAX_KNOWLEDGE_DOCUMENT_BYTES = 2 * 1024 * 1024;\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 + 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 /**\n * Provenance of the hit. All live results are `\"doc\"`; `\"fact\"` only ever\n * appears for legacy vectors indexed before the facts primitive was removed\n * (the search index stays tolerant of them). Treat every hit as a doc.\n */\n source: \"doc\" | \"fact\";\n /** The canonical file under shared/<scope>/ (present on doc hits). */\n filePath?: string;\n /** Legacy-only: the id of a pre-removal fact vector. */\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 type ChangeRequestResourceType = \"doc\" | \"profile\";\nexport type ChangeRequestOperation = \"create\" | \"update\" | \"delete\";\nexport type ChangeRequestStatus =\n | \"open\"\n | \"approved\"\n | \"rejected\"\n | \"withdrawn\"\n | \"superseded\";\nexport type ChangeRequestActorKind = \"human\" | \"agent\";\n\n/** Public projection of a change request (mirrors `PublicChangeRequest` in services/org). */\nexport interface KnowledgeChangeRequest {\n changeRequestId: string;\n scopeType: KnowledgeScopeType;\n scopeId: string;\n resourceType: ChangeRequestResourceType;\n operation: ChangeRequestOperation;\n targetPath: string | null;\n baseVersionId: string | null;\n proposedContentType: string | null;\n status: ChangeRequestStatus;\n proposerId: string;\n proposerKind: ChangeRequestActorKind;\n rationale: string;\n reviewerId: string | null;\n reviewerKind: ChangeRequestActorKind | null;\n reviewedAt: string | null;\n reviewNote: string | null;\n appliedRef: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/** Per-type proposal payload for `proposeScopeChange`. */\nexport interface ProposeScopeChangeInput {\n resourceType: ChangeRequestResourceType;\n operation: ChangeRequestOperation;\n /** Why the change is proposed — shown to the reviewer. */\n rationale: string;\n /** doc: the path the proposal applies to (e.g. designs/data-center.md). */\n targetPath?: string;\n /** doc create/update: the staged body to upload (markdown or other text). */\n content?: string;\n /** doc create/update: content type of the staged body (default text/markdown). */\n contentType?: string;\n /** profile: the proposed value ({ about, description, links }). */\n proposedValue?: unknown;\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// ─── Knowledge resources (org/team/project) ──────────────\n//\n// Scoped knowledge ABOUT a thing being worked on. Search hits\n// `services/knowledge` (RAG); docs + 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\nexport class KnowledgeApi extends ApiBase {\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.transport.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.transport.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.transport.request<KnowledgeProfile>(\n `/agent/org/profile/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n );\n }\n\n // Propose a change to a scope's knowledge instead of writing it directly.\n // Use this ONLY when the agent is not a member of the target scope (direct\n // resource_write_* is refused there) or otherwise cannot write directly —\n // the proposal is inert until a scope reviewer approves it. Create is gated\n // on a valid agent token only (no membership needed), which is what lets a\n // non-member contribute.\n\n /**\n * Open a change request against a scope's knowledge resource. For a doc\n * create/update, `services/org` returns a presigned staging PUT; this method\n * uploads the proposed `content` to it (echoing the same Content-Type that\n * was signed), mirroring `writeScopeDoc`. The staged body is applied to the\n * canonical doc — attributed to this agent — only when a reviewer approves.\n */\n async proposeScopeChange(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n input: ProposeScopeChangeInput,\n ): Promise<KnowledgeChangeRequest> {\n const isDocBody =\n input.resourceType === \"doc\" && input.operation !== \"delete\";\n const contentType = input.contentType ?? \"text/markdown\";\n\n const result = await this.transport.request<{\n changeRequest: KnowledgeChangeRequest;\n uploadUrl?: string;\n requiredHeaders?: Record<string, string>;\n }>(\n `/agent/org/change-requests/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}`,\n {\n method: \"POST\",\n body: JSON.stringify({\n resourceType: input.resourceType,\n operation: input.operation,\n rationale: input.rationale,\n targetPath: input.targetPath,\n proposedContentType: isDocBody ? contentType : undefined,\n proposedValue: input.proposedValue,\n }),\n },\n );\n\n // doc create/update: stage the proposed body at the returned presigned PUT.\n if (isDocBody && result.uploadUrl) {\n const putHeaders = new Headers(result.requiredHeaders ?? {});\n // The staging PUT signed `ContentType` into the URL — the PUT must echo\n // the exact same value or S3 rejects with SignatureDoesNotMatch.\n putHeaders.set(\"Content-Type\", contentType);\n const res = await fetch(result.uploadUrl, {\n method: \"PUT\",\n body: input.content ?? \"\",\n headers: putHeaders,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!res.ok) {\n await res.text();\n throw new Error(`Change-request body upload failed (${String(res.status)})`);\n }\n }\n\n return result.changeRequest;\n }\n\n /**\n * List the agent's OWN change requests in a scope (filtered server-side to\n * this agent as proposer). Pass `status` to narrow to open / approved / etc.\n */\n async listScopeChangeRequests(\n scopeType: KnowledgeScopeType,\n scopeId: string,\n opts?: { status?: ChangeRequestStatus; limit?: number; cursor?: string },\n ): Promise<{ changeRequests: KnowledgeChangeRequest[]; nextCursor: string | null }> {\n const qs = new URLSearchParams();\n if (opts?.status) qs.set(\"status\", opts.status);\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.transport.request<{ changeRequests: KnowledgeChangeRequest[]; nextCursor: string | null }>(\n `/agent/org/change-requests/${encodeURIComponent(scopeType)}/${encodeURIComponent(scopeId)}${query ? `?${query}` : \"\"}`,\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.transport.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 opts?: { maxBytes?: number },\n ): Promise<{ filePath: string; text: string }> {\n const maxBytes = opts?.maxBytes ?? MAX_KNOWLEDGE_DOCUMENT_BYTES;\n if (\n !Number.isInteger(maxBytes) ||\n maxBytes < 1 ||\n maxBytes > MAX_KNOWLEDGE_DOCUMENT_BYTES\n ) {\n throw new RangeError(\n `maxBytes must be an integer from 1 to ${String(MAX_KNOWLEDGE_DOCUMENT_BYTES)}`,\n );\n }\n const { downloadUrl } = await this.transport.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.body?.cancel().catch(() => undefined);\n throw new Error(`Doc download failed (${String(res.status)})`);\n }\n const text = await readBoundedUtf8(res, maxBytes);\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.transport.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\nasync function readBoundedUtf8(response: Response, maxBytes: number): Promise<string> {\n const declaredLength = response.headers.get(\"content-length\");\n if (declaredLength !== null && /^\\d+$/u.test(declaredLength) && Number(declaredLength) > maxBytes) {\n await response.body?.cancel().catch(() => undefined);\n throw documentTooLargeError(maxBytes);\n }\n if (response.body === null) return \"\";\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n let complete = false;\n try {\n while (!complete) {\n const { done, value } = await reader.read();\n if (done) {\n complete = true;\n continue;\n }\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined);\n throw documentTooLargeError(maxBytes);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n try {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n throw new Error(\"Knowledge document is not valid UTF-8 text\");\n }\n}\n\nfunction documentTooLargeError(maxBytes: number): Error & { code: string } {\n const error = new Error(\n `Knowledge document exceeds the ${String(maxBytes)} byte read limit`,\n ) as Error & { code: string };\n error.name = \"KnowledgeDocumentTooLargeError\";\n error.code = \"KNOWLEDGE_DOCUMENT_TOO_LARGE\";\n return error;\n}\n","/**\n * Cloud memory methods (Turbopuffer vectors + DynamoDB knowledge graph)\n * for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Memory ──────────────────────────────────────────────\n//\n// Cloud memory (Turbopuffer vectors + DynamoDB knowledge graph).\n// The agent API derives tenantId + agentId from the agent token.\n\nexport class MemoryApi extends ApiBase {\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.transport.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.transport.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 }, ingestEpoch?: number): Promise<{ queued: boolean; messageCount: number }> {\n return this.transport.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 // Per-boot monotonic epoch from live auto-capture. Lets the memory\n // service reset the per-session high-water mark after a daemon restart\n // (which resets the client's message-index counter) so post-restart\n // captures aren't silently dropped. Omitted by backfill, which uses\n // stable file-position indices. Only sent when provided.\n ...(ingestEpoch !== undefined ? { ingestEpoch } : {}),\n messages,\n metadata,\n }),\n });\n }\n\n async memoryLoadContext(tier?: number, topicHint?: string): Promise<{\n tier: number;\n facts: { subject: string; predicate: string; object: string; since: string }[];\n memories: { text: string; topic: string; subtopic: string; score: number }[];\n tokenEstimate: number;\n formatted: string;\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.transport.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.transport.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.transport.request(\"/agent/memory/navigate\");\n }\n\n async memoryDelete(memoryId: string): Promise<{ deleted: boolean }> {\n return this.transport.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.transport.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.transport.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<{\n synced: boolean;\n syncedAt?: string;\n sessionsBackfillSynced?: boolean;\n sessionsBackfillSyncedAt?: string;\n }> {\n return this.transport.request(\"/agent/memory/bootstrap-status\");\n }\n\n async memoryBootstrapStatusMark(\n scope?: \"files\" | \"sessions\",\n ): Promise<{ synced: true; syncedAt: string }> {\n return this.transport.request(\"/agent/memory/bootstrap-status\", {\n method: \"POST\",\n ...(scope ? { body: JSON.stringify({ scope }) } : {}),\n });\n }\n}\n","/**\n * Mobile (numbers / SMS / calls) + WhatsApp methods (services/mobile)\n * for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Mobile / WhatsApp types (services/mobile) ────────────\n\n/** Response of GET /mobile/numbers for an agent (services/mobile). */\nexport interface MobileNumberInfo {\n phoneNumber: string;\n countryCode: string;\n monthlyPrice?: number;\n status: string;\n errorMessage?: string;\n}\n\n/** One purchasable number from GET /mobile/numbers/search. */\nexport interface MobileAvailableNumber {\n number: string;\n friendlyName: string;\n locality: string;\n region: string;\n country: string;\n}\n\n/** Approved WhatsApp content template from GET /mobile/whatsapp/templates. */\nexport interface WhatsAppTemplate {\n contentSid: string;\n name: string;\n language: string;\n body: string;\n variables: Record<string, string>;\n category?: string;\n}\n\n// ─── Mobile (numbers / SMS / calls) ──────────────────────\n//\n// services/mobile routes. Dual-auth endpoints — with an agent\n// token the backend resolves agentId + tenantId from the token,\n// so no agentId is sent.\n\nexport class MobileApi extends ApiBase {\n async getMobileNumber(): Promise<MobileNumberInfo> {\n return this.transport.request(\"/mobile/numbers\");\n }\n\n async searchMobileNumbers(args?: {\n country?: string;\n query?: string;\n }): Promise<{ numbers: MobileAvailableNumber[]; monthlyPrice: number }> {\n const qs = new URLSearchParams();\n if (args?.country) qs.set(\"country\", args.country);\n if (args?.query) qs.set(\"query\", args.query);\n const query = qs.toString();\n return this.transport.request(`/mobile/numbers/search${query ? `?${query}` : \"\"}`);\n }\n\n async assignMobileNumber(args: {\n phoneNumber: string;\n countryCode: string;\n }): Promise<{ phoneNumber: string; countryCode: string; status: \"pending\" }> {\n return this.transport.request(\"/mobile/numbers/assign\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async releaseMobileNumber(): Promise<{ released: true }> {\n return this.transport.request(\"/mobile/numbers/release\", {\n method: \"POST\",\n body: JSON.stringify({}),\n });\n }\n\n async sendSms(args: { to: string; body: string }): Promise<{ sent: true; sid: string }> {\n return this.transport.request(\"/mobile/sms/send\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async startOutboundCall(args: { to: string }): Promise<{ callSid: string; status: string }> {\n return this.transport.request(\"/mobile/calls/outbound\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n // ─── WhatsApp ────────────────────────────────────────────\n\n async getWhatsAppSession(to: string): Promise<{ active: boolean; expiresAt?: string }> {\n return this.transport.request(`/mobile/whatsapp/session?to=${encodeURIComponent(to)}`);\n }\n\n async sendWhatsAppMessage(args: { to: string; body: string }): Promise<{ sent: true; sid: string }> {\n return this.transport.request(\"/mobile/whatsapp/send\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async sendWhatsAppTemplate(args: {\n to: string;\n contentSid: string;\n contentVariables: Record<string, string>;\n bodyPreview?: string;\n }): Promise<{ sent: true; sid: string }> {\n return this.transport.request(\"/mobile/whatsapp/send-template\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async listWhatsAppTemplates(): Promise<{ templates: WhatsAppTemplate[] }> {\n return this.transport.request(\"/mobile/whatsapp/templates\");\n }\n}\n","/**\n * Remote (interactive relay) methods — browser co-browse / terminal takeover\n * sessions brokered by the relay service. Ported from main's monolith\n * additions (b5e3c1e3) into the domain-split layout.\n */\nimport { ApiBase } from \"../transport.js\";\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\nexport class RemoteApi extends ApiBase {\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.transport.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.transport.request(`/agent/remote/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async completeRemoteSession(sessionId: string): Promise<{ ok: boolean }> {\n return this.transport.request(`/agent/remote/sessions/${encodeURIComponent(sessionId)}/complete`, {\n method: \"POST\",\n body: JSON.stringify({}),\n });\n }\n}\n","/**\n * Web/image/news search methods (services/search) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── News types (metered News MCP) ───────────────────────\n\n/**\n * The broad-news providers behind the metered `services/news` Lambda. The\n * server validates this with a zod enum; a value outside the union is an\n * unpriceable product, so keep the literal union in lockstep with the service.\n */\nexport type NewsProvider = \"apitube\" | \"newsdata\";\n\n/** One normalized article. `sentiment` is provider-shaped (APITube supplies it). */\nexport interface NewsArticle {\n title: string;\n url: string;\n source: string;\n publishedAt: string;\n snippet: string;\n sentiment?: unknown;\n}\n\n/** Provider-agnostic result — the server normalizes every adapter to this. */\nexport interface NewsResult {\n articles: NewsArticle[];\n provider: string;\n}\n\n// ─── Search ──────────────────────────────────────────────\n\nexport class SearchApi extends ApiBase {\n async searchWeb(params: {\n query: string;\n count?: number;\n offset?: number;\n country?: string;\n freshness?: string;\n }, options?: { signal?: AbortSignal }): Promise<unknown> {\n return this.transport.request(\"/agent/search/web\", {\n method: \"POST\",\n body: JSON.stringify(params),\n }, { signal: options?.signal });\n }\n\n async searchImages(params: {\n query: string;\n count?: number;\n }, options?: { signal?: AbortSignal }): Promise<unknown> {\n return this.transport.request(\"/agent/search/images\", {\n method: \"POST\",\n body: JSON.stringify(params),\n }, { signal: options?.signal });\n }\n\n async searchNews(params: {\n query: string;\n count?: number;\n offset?: number;\n freshness?: string;\n }, options?: { signal?: AbortSignal }): Promise<unknown> {\n return this.transport.request(\"/agent/search/news\", {\n method: \"POST\",\n body: JSON.stringify(params),\n }, { signal: options?.signal });\n }\n\n // ─── News (metered, provider-pluggable) ──────────────────\n //\n // Broad web-search-style news via the `services/news` money-path Lambda\n // (APITube / NewsData behind a `provider` arg). DISTINCT from `searchNews`\n // above (Brave, `/agent/search/news`) — these hit `/agent/news/...`. Provider\n // keys + metering live server-side; the client only forwards a typed body.\n\n /** Search news across the selected provider's corpus. → POST /agent/news/search */\n async newsSearch(params: {\n query: string;\n provider?: NewsProvider;\n source?: string;\n from?: string;\n to?: string;\n language?: string;\n category?: string;\n limit?: number;\n }): Promise<NewsResult> {\n return this.transport.request<NewsResult>(\"/agent/news/search\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n /** Top headlines for the selected provider. → POST /agent/news/headlines */\n async newsHeadlines(params?: {\n provider?: NewsProvider;\n category?: string;\n source?: string;\n language?: string;\n limit?: number;\n }): Promise<NewsResult> {\n return this.transport.request<NewsResult>(\"/agent/news/headlines\", {\n method: \"POST\",\n body: JSON.stringify(params ?? {}),\n });\n }\n}\n","/**\n * Per-scope secret store methods (envelope CRUD + KMS proxy) for the\n * Agent API client.\n */\n\nimport type {\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\";\nimport { ApiBase } from \"../transport.js\";\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\nexport class SecretsApi extends ApiBase {\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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.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.transport.request<{ scopes: ScopeInfo[] }>(\"/agent/secrets/scopes\");\n return resp.scopes;\n }\n}\n","/**\n * Self-identity methods — the agent customizing its OWN identity (name,\n * avatar, voice). agentId + tenantId are resolved from the token, so no id\n * appears in the request. Ported from main's monolith additions into the\n * domain-split layout.\n */\nimport { ApiBase, isTransientRequestError, sleep } from \"../transport.js\";\n\n// Async avatar-generation job polling — same contract as image generation:\n// gen runs off-request so a multi-minute poll (in the daemon, not a Lambda)\n// avoids the API Gateway 30s ceiling.\nconst AVATAR_POLL_INTERVAL_MS = 2_000;\nconst AVATAR_JOB_TIMEOUT_MS = 180_000;\n\n// ─── Self identity types ─────────────────────────────────\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\nexport class SelfApi extends ApiBase {\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.transport.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 (same reason as `generateImage`): avatar gen runs `gpt-image-1`\n * (30–60s) which exceeds the API Gateway 30s ceiling, so this enqueues a job\n * (`POST /agent/avatar/generate` → `jobId`) then polls (`GET /agent/avatar/{jobId}`)\n * until the avatar is set. Signature unchanged — the plugin is unaffected.\n */\n async generateAvatar(args: { prompt: string }): Promise<AgentSelf> {\n // Enqueue via a NON-retrying POST — `scheduleJob` is not idempotent, so a\n // retried enqueue would double-run the metered job.\n const { jobId } = await this.transport.request<{ jobId: string }>(\n \"/agent/avatar/generate\",\n { method: \"POST\", body: JSON.stringify(args) },\n { retry: false },\n );\n\n const deadline = Date.now() + AVATAR_JOB_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await sleep(AVATAR_POLL_INTERVAL_MS);\n let job: {\n status: \"pending\" | \"running\" | \"completed\" | \"failed\";\n agent?: AgentSelf;\n error?: string;\n };\n try {\n job = await this.transport.request(`/agent/avatar/${jobId}`);\n } catch (error) {\n if (isTransientRequestError(error)) continue;\n throw error;\n }\n\n if (job.status === \"completed\") {\n if (!job.agent) throw new Error(\"Avatar generation completed without an agent\");\n return job.agent;\n }\n if (job.status === \"failed\") {\n const detail = job.error ? `: ${job.error.split(\"\\n\")[0]}` : \"\";\n throw new Error(`Avatar generation failed${detail}`);\n }\n // pending | running → keep polling\n }\n throw new Error(\"Avatar generation timed out\");\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.transport.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.transport.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.transport.request<{ voices: AgentVoice[] }>(\"/agent/voices\");\n }\n}\n","/**\n * Voice one-shot TTS / STT methods.\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 and go\n * through the transport's `rawRequest` instead. Metering to the tenant credit\n * pool happens server-side; the caller just gets audio (TTS) or a transcript\n * (STT). Ported from main's monolith additions into the domain-split layout.\n */\nimport { ApiBase } from \"../transport.js\";\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 VoiceApi extends ApiBase {\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.transport.rawRequest(\"/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.transport.rawRequest(\"/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","/**\n * Sync + shared (org/team/project) file methods for the Agent API client.\n */\n\nimport { ApiBase, encodeFilePath } from \"../transport.js\";\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// ─── 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\nexport class SyncApi extends ApiBase {\n async syncRegister(args?: { displayName?: string }): Promise<{ agent: SyncAgentInfo }> {\n return this.transport.request(\"/agent/sync/register\", {\n method: \"POST\",\n body: JSON.stringify(args ?? {}),\n });\n }\n\n async syncGetManifest(): Promise<SyncManifest> {\n return this.transport.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.transport.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.transport.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.transport.request(\"/agent/sync/reconstruct\", {\n method: \"POST\",\n body: JSON.stringify(args),\n });\n }\n\n async syncGetStats(): Promise<SyncAgentStats> {\n return this.transport.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.transport.request(`/agent/sync/files${query ? `?${query}` : \"\"}`);\n }\n\n async syncListSessions(): Promise<{ sessions: SyncSessionEntry[] }> {\n return this.transport.request(\"/agent/sync/sessions\");\n }\n\n async syncGetSession(sessionId: string): Promise<SyncSessionContent> {\n return this.transport.request(`/agent/sync/sessions/${encodeURIComponent(sessionId)}`);\n }\n\n async syncDeleteFile(filePath: string): Promise<{ removed: boolean }> {\n return this.transport.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 limit?: number;\n cursor?: string;\n }): Promise<{ files: SharedFileEntry[]; nextCursor: string | null }> {\n const params = new URLSearchParams();\n if (args.limit !== undefined) params.set(\"limit\", String(args.limit));\n if (args.cursor) params.set(\"cursor\", args.cursor);\n const query = params.toString();\n return this.transport.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}${query ? `?${query}` : \"\"}`,\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.transport.request(\n `/agent/org/files/${encodeURIComponent(args.scope)}/${encodeURIComponent(args.scopeId)}/download/${encodeFilePath(args.filePath)}`,\n );\n }\n}\n","/**\n * Microsoft Teams adapter methods (services/microsoft bot credentials +\n * messaging) for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\nexport class TeamsApi extends ApiBase {\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.transport.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.transport.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.transport.request(\"/agent/microsoft/channels\");\n }\n}\n","/**\n * Workspace + template file methods for the Agent API client.\n */\n\nimport { ApiBase } from \"../transport.js\";\n\n// ─── Workspace types ─────────────────────────────────────\n\n/** Response of GET /agent/workspace (services/agents). */\nexport interface AgentWorkspaceInfo {\n templateKey?: string;\n defaultModel?: string;\n installedFrom?: { templateKey: string; authorTenantId: string; version: number };\n runtime?: string;\n teams?: { teamId: string; name: string; description?: string; parentTeamId?: string }[];\n projects?: { projectId: string; name: string; description?: string; status: string; parentProjectId?: string }[];\n teamIds?: string[];\n projectIds?: string[];\n}\n\nexport class WorkspaceApi extends ApiBase {\n /**\n * GET /agent/workspace — workspace config for the authenticated agent\n * (template assignment, default model, org roster).\n */\n async getWorkspace(): Promise<AgentWorkspaceInfo> {\n return this.transport.request<AgentWorkspaceInfo>(\"/agent/workspace\");\n }\n\n /**\n * GET /templates/{key}/files — persona/workspace file contents for a\n * template the agent has access to. Pass `version` to pin to the version\n * the agent was installed from (omit → the endpoint resolves `latest`).\n */\n async getTemplateFiles(\n templateKey: string,\n opts?: { version?: number },\n ): Promise<{ files: Record<string, string> }> {\n const query = opts?.version !== undefined ? `?version=${String(opts.version)}` : \"\";\n return this.transport.request<{ files: Record<string, string> }>(\n `/agent/templates/${encodeURIComponent(templateKey)}/files${query}`,\n );\n }\n}\n","/** Agent self-service webhook management methods. */\nimport { ApiBase } from \"../transport.js\";\n\nexport interface AgentWebhook {\n webhookId: string;\n tenantId: string;\n agentId: string;\n name: string;\n provider: string;\n active: boolean;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreatedAgentWebhook extends AgentWebhook {\n url: string;\n signingSecret: string;\n}\n\nexport interface AgentWebhookDelivery {\n deliveryId: string;\n webhookId: string;\n status: string;\n attempts: number;\n createdAt: string;\n deliveredAt?: string;\n}\n\nexport class WebhooksApi extends ApiBase {\n async createWebhook(args: {\n name: string;\n provider?: \"generic\" | \"github\" | \"stripe\" | \"slack\";\n }): Promise<CreatedAgentWebhook> {\n return this.transport.request<CreatedAgentWebhook>(\"/agent/webhooks\", {\n method: \"POST\",\n body: JSON.stringify(args),\n }, { retry: false });\n }\n\n async listWebhooks(): Promise<AgentWebhook[]> {\n const result = await this.transport.request<{ webhooks: AgentWebhook[] }>(\"/agent/webhooks\");\n return result.webhooks;\n }\n\n async deleteWebhook(webhookId: string): Promise<{ webhookId: string; active: false }> {\n return this.transport.request(`/agent/webhooks/${encodeURIComponent(webhookId)}`, {\n method: \"DELETE\",\n });\n }\n\n async rotateWebhookSecret(webhookId: string): Promise<{ webhookId: string; signingSecret: string }> {\n return this.transport.request(\n `/agent/webhooks/${encodeURIComponent(webhookId)}/rotate`,\n { method: \"POST\" },\n { retry: false },\n );\n }\n\n async listWebhookDeliveries(webhookId: string): Promise<AgentWebhookDelivery[]> {\n const result = await this.transport.request<{ deliveries: AgentWebhookDelivery[] }>(\n `/agent/webhooks/${encodeURIComponent(webhookId)}/deliveries`,\n );\n return result.deliveries;\n }\n}\n","/**\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 {\n installToolErrorCapture,\n type ToolCaptureApi,\n type InstallToolErrorCaptureOptions,\n} from \"./tool-error-capture.js\";\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\nexport type { AgentApiClientConfig } from \"./transport.js\";\nexport type { AgentWorkspaceInfo } from \"./domains/workspace.js\";\nexport type {\n SyncAgentInfo,\n SyncManifestEntry,\n SyncManifest,\n SyncPresignedUrl,\n SyncConfirmedUpload,\n SyncReconstructFile,\n SyncReconstructBundle,\n SyncAgentStats,\n SyncFileEntry,\n SyncSessionEntry,\n SyncSessionContent,\n SharedFileEntry,\n} from \"./domains/sync.js\";\nexport type {\n KnowledgeScopeType,\n KnowledgeScope,\n KnowledgeSearchHit,\n KnowledgeSearchResult,\n KnowledgeProfileLink,\n KnowledgeProfile,\n KnowledgeDoc,\n ChangeRequestResourceType,\n ChangeRequestOperation,\n ChangeRequestStatus,\n ChangeRequestActorKind,\n KnowledgeChangeRequest,\n ProposeScopeChangeInput,\n} from \"./domains/knowledge.js\";\nexport type {\n MobileNumberInfo,\n MobileAvailableNumber,\n WhatsAppTemplate,\n} from \"./domains/mobile.js\";\nexport type { RemoteSessionInfo } from \"./domains/remote.js\";\nexport type {\n AgentVoiceConfig,\n AgentSelf,\n AgentAvatarPresign,\n AgentVoice,\n} from \"./domains/self.js\";\nexport type {\n VoiceTtsModel,\n VoiceTtsArgs,\n VoiceTtsResult,\n VoiceSttArgs,\n VoiceSttResult,\n} from \"./domains/voice.js\";\nexport type {\n NewsProvider,\n NewsArticle,\n NewsResult,\n} from \"./domains/search.js\";\nexport type {\n AgentWebhook,\n CreatedAgentWebhook,\n AgentWebhookDelivery,\n} from \"./domains/webhooks.js\";\n\nimport { AgentApiTransport, ApiBase, type AgentApiClientConfig } from \"./transport.js\";\nimport { ChatApi } from \"./domains/chat.js\";\nimport { ConnectCredentialsApi } from \"./domains/connect-credentials.js\";\nimport { DatabaseApi } from \"./domains/database.js\";\nimport { IdentityApi } from \"./domains/identity.js\";\nimport { ImagesApi } from \"./domains/images.js\";\nimport { IntegrationsApi } from \"./domains/integrations.js\";\nimport { KnowledgeApi } from \"./domains/knowledge.js\";\nimport { MemoryApi } from \"./domains/memory.js\";\nimport { MobileApi } from \"./domains/mobile.js\";\nimport { RemoteApi } from \"./domains/remote.js\";\nimport { SearchApi } from \"./domains/search.js\";\nimport { SecretsApi } from \"./domains/secrets.js\";\nimport { SelfApi } from \"./domains/self.js\";\nimport { VoiceApi } from \"./domains/voice.js\";\nimport { SyncApi } from \"./domains/sync.js\";\nimport { TeamsApi } from \"./domains/teams.js\";\nimport { WorkspaceApi } from \"./domains/workspace.js\";\nimport { WebhooksApi } from \"./domains/webhooks.js\";\n\n// The client is assembled from per-domain method groups (each an ApiBase\n// subclass under ./domains/). Declaration merging presents the union as one\n// flat class type — the public surface is unchanged from the pre-split\n// single-class layout — while applyMixins() copies the prototype methods\n// onto AgentApiClient at module load.\n// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -- mixin assembly; applyMixins() below supplies every merged member\nexport interface AgentApiClient\n extends SyncApi,\n IntegrationsApi,\n WorkspaceApi,\n ConnectCredentialsApi,\n TeamsApi,\n ChatApi,\n SecretsApi,\n IdentityApi,\n MemoryApi,\n SearchApi,\n KnowledgeApi,\n DatabaseApi,\n MobileApi,\n RemoteApi,\n SelfApi,\n VoiceApi,\n ImagesApi,\n WebhooksApi {}\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -- see interface note above\nexport class AgentApiClient extends ApiBase {\n constructor(config: AgentApiClientConfig) {\n super(new AgentApiTransport(config));\n }\n}\n\n/** Copy each domain group's prototype methods onto the client class. */\nfunction applyMixins(derived: { prototype: object }, bases: { prototype: object }[]): void {\n for (const base of bases) {\n for (const name of Object.getOwnPropertyNames(base.prototype)) {\n if (name === \"constructor\") continue;\n const descriptor = Object.getOwnPropertyDescriptor(base.prototype, name);\n if (descriptor) Object.defineProperty(derived.prototype, name, descriptor);\n }\n }\n}\n\napplyMixins(AgentApiClient, [\n SyncApi,\n IntegrationsApi,\n WorkspaceApi,\n ConnectCredentialsApi,\n TeamsApi,\n ChatApi,\n SecretsApi,\n IdentityApi,\n MemoryApi,\n SearchApi,\n KnowledgeApi,\n DatabaseApi,\n MobileApi,\n RemoteApi,\n SelfApi,\n VoiceApi,\n ImagesApi,\n WebhooksApi,\n]);\n"],"mappings":"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"}
|