@alfe.ai/claude-code-host 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +49 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +50 -0
- package/dist/host-config.cjs +1346 -0
- package/dist/host-config.js +1221 -0
- package/dist/index.cjs +23 -0
- package/dist/index.d.cts +2055 -0
- package/dist/index.d.ts +2055 -0
- package/dist/index.js +2 -0
- package/package.json +46 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,2055 @@
|
|
|
1
|
+
import { ChangelogEntry, EncryptedEnvelopeV1, FieldEnvelope, FieldFormat, FieldSensitivity, GeneratedDataKey, IntegrationConfigResult, IntegrationInstall, RegistryEntry, ScopeInfo, SecretAggregate, SecretCategory, SecretMetadata, SecretScope } from "@alfe/types";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
//#region ../agent-api-client/dist/index.d.ts
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=tool-error-capture.d.ts.map
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/index.d.ts
|
|
9
|
+
interface AgentApiClientConfig {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
apiUrl: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The broad-news providers behind the metered `services/news` Lambda. The
|
|
15
|
+
* server validates this with a zod enum; a value outside the union is an
|
|
16
|
+
* unpriceable product, so keep the literal union in lockstep with the service.
|
|
17
|
+
*/
|
|
18
|
+
type NewsProvider = "apitube" | "newsdata";
|
|
19
|
+
/** One normalized article. `sentiment` is provider-shaped (APITube supplies it). */
|
|
20
|
+
interface NewsArticle {
|
|
21
|
+
title: string;
|
|
22
|
+
url: string;
|
|
23
|
+
source: string;
|
|
24
|
+
publishedAt: string;
|
|
25
|
+
snippet: string;
|
|
26
|
+
sentiment?: unknown;
|
|
27
|
+
}
|
|
28
|
+
/** Provider-agnostic result — the server normalizes every adapter to this. */
|
|
29
|
+
interface NewsResult {
|
|
30
|
+
articles: NewsArticle[];
|
|
31
|
+
provider: string;
|
|
32
|
+
}
|
|
33
|
+
interface RemoteSessionInfo {
|
|
34
|
+
sessionId: string;
|
|
35
|
+
agentId: string;
|
|
36
|
+
surface: "browser" | "terminal";
|
|
37
|
+
status: "agent_driving" | "awaiting_human" | "human_in_control" | "resuming" | "completed" | "expired" | "failed";
|
|
38
|
+
url?: string;
|
|
39
|
+
instructions?: string;
|
|
40
|
+
requestedAt?: string;
|
|
41
|
+
}
|
|
42
|
+
interface SyncAgentInfo {
|
|
43
|
+
agentId: string;
|
|
44
|
+
tenantId: string;
|
|
45
|
+
displayName: string;
|
|
46
|
+
s3Prefix: string;
|
|
47
|
+
status: "stale" | "syncing" | "synced";
|
|
48
|
+
fileCount?: number;
|
|
49
|
+
totalSize?: number;
|
|
50
|
+
lastSync?: string;
|
|
51
|
+
}
|
|
52
|
+
interface SyncManifestEntry {
|
|
53
|
+
hash: string;
|
|
54
|
+
size: number;
|
|
55
|
+
modified: string;
|
|
56
|
+
etag?: string;
|
|
57
|
+
storageClass?: string;
|
|
58
|
+
compressed?: boolean;
|
|
59
|
+
}
|
|
60
|
+
interface SyncManifest {
|
|
61
|
+
version: 1;
|
|
62
|
+
agentId: string;
|
|
63
|
+
lastSync: string;
|
|
64
|
+
files: Record<string, SyncManifestEntry>;
|
|
65
|
+
}
|
|
66
|
+
interface SyncPresignedUrl {
|
|
67
|
+
path: string;
|
|
68
|
+
url: string;
|
|
69
|
+
expiresAt: string;
|
|
70
|
+
}
|
|
71
|
+
interface SyncConfirmedUpload {
|
|
72
|
+
filePath: string;
|
|
73
|
+
hash: string;
|
|
74
|
+
size: number;
|
|
75
|
+
storageClass: "STANDARD" | "GLACIER_IR";
|
|
76
|
+
syncedAt: string;
|
|
77
|
+
}
|
|
78
|
+
interface SyncReconstructFile {
|
|
79
|
+
path: string;
|
|
80
|
+
size: number;
|
|
81
|
+
url: string;
|
|
82
|
+
storageClass?: string;
|
|
83
|
+
compressed?: boolean;
|
|
84
|
+
}
|
|
85
|
+
interface SyncReconstructBundle {
|
|
86
|
+
agentId: string;
|
|
87
|
+
mode: "full" | "active" | "memory";
|
|
88
|
+
fileCount: number;
|
|
89
|
+
totalSize: number;
|
|
90
|
+
files: SyncReconstructFile[];
|
|
91
|
+
expiresAt: string;
|
|
92
|
+
}
|
|
93
|
+
interface SyncAgentStats {
|
|
94
|
+
agentId: string;
|
|
95
|
+
standardBytes: number;
|
|
96
|
+
glacierBytes: number;
|
|
97
|
+
fileCount: number;
|
|
98
|
+
lastSyncAt: string | null;
|
|
99
|
+
}
|
|
100
|
+
interface SyncFileEntry {
|
|
101
|
+
filePath: string;
|
|
102
|
+
size: number;
|
|
103
|
+
modified: string;
|
|
104
|
+
contentHash: string;
|
|
105
|
+
storageClass?: string;
|
|
106
|
+
compressed?: boolean;
|
|
107
|
+
}
|
|
108
|
+
interface SyncSessionEntry {
|
|
109
|
+
sessionId: string;
|
|
110
|
+
size: number;
|
|
111
|
+
lastModified: string;
|
|
112
|
+
storageClass?: string;
|
|
113
|
+
isArchived: boolean;
|
|
114
|
+
}
|
|
115
|
+
interface SyncSessionContent {
|
|
116
|
+
sessionId: string;
|
|
117
|
+
content: string;
|
|
118
|
+
compressed: boolean;
|
|
119
|
+
}
|
|
120
|
+
interface SharedFileEntry {
|
|
121
|
+
filePath: string;
|
|
122
|
+
fileName: string;
|
|
123
|
+
size: number;
|
|
124
|
+
contentType?: string;
|
|
125
|
+
}
|
|
126
|
+
type KnowledgeScopeType = "org" | "team" | "project";
|
|
127
|
+
interface KnowledgeScope {
|
|
128
|
+
scopeType: KnowledgeScopeType;
|
|
129
|
+
scopeId: string;
|
|
130
|
+
name: string;
|
|
131
|
+
}
|
|
132
|
+
interface KnowledgeSearchHit {
|
|
133
|
+
id: string;
|
|
134
|
+
text: string;
|
|
135
|
+
/** Normalized relevance in (0,1]; higher = closer. */
|
|
136
|
+
score: number;
|
|
137
|
+
scopeType: KnowledgeScopeType;
|
|
138
|
+
scopeId: string;
|
|
139
|
+
/**
|
|
140
|
+
* Provenance of the hit. All live results are `"doc"`; `"fact"` only ever
|
|
141
|
+
* appears for legacy vectors indexed before the facts primitive was removed
|
|
142
|
+
* (the search index stays tolerant of them). Treat every hit as a doc.
|
|
143
|
+
*/
|
|
144
|
+
source: "doc" | "fact";
|
|
145
|
+
/** The canonical file under shared/<scope>/ (present on doc hits). */
|
|
146
|
+
filePath?: string;
|
|
147
|
+
/** Legacy-only: the id of a pre-removal fact vector. */
|
|
148
|
+
factId?: string;
|
|
149
|
+
}
|
|
150
|
+
interface KnowledgeSearchResult {
|
|
151
|
+
results: KnowledgeSearchHit[];
|
|
152
|
+
/** True when fan-out breadth was capped (more member scopes than the cap). */
|
|
153
|
+
truncatedScopes: boolean;
|
|
154
|
+
}
|
|
155
|
+
interface KnowledgeProfileLink {
|
|
156
|
+
label: string;
|
|
157
|
+
url: string;
|
|
158
|
+
}
|
|
159
|
+
interface KnowledgeProfile {
|
|
160
|
+
scopeType: KnowledgeScopeType;
|
|
161
|
+
scopeId: string;
|
|
162
|
+
about: string | null;
|
|
163
|
+
description: string | null;
|
|
164
|
+
links: KnowledgeProfileLink[];
|
|
165
|
+
updatedAt: string | null;
|
|
166
|
+
updatedBy: string | null;
|
|
167
|
+
}
|
|
168
|
+
interface KnowledgeDoc {
|
|
169
|
+
filePath: string;
|
|
170
|
+
fileName: string;
|
|
171
|
+
contentType?: string;
|
|
172
|
+
size: number;
|
|
173
|
+
uploadedBy?: string;
|
|
174
|
+
createdAt: string;
|
|
175
|
+
updatedAt: string;
|
|
176
|
+
}
|
|
177
|
+
type ChangeRequestResourceType = "doc" | "profile";
|
|
178
|
+
type ChangeRequestOperation = "create" | "update" | "delete";
|
|
179
|
+
type ChangeRequestStatus = "open" | "approved" | "rejected" | "withdrawn" | "superseded";
|
|
180
|
+
type ChangeRequestActorKind = "human" | "agent";
|
|
181
|
+
/** Public projection of a change request (mirrors `PublicChangeRequest` in services/org). */
|
|
182
|
+
interface KnowledgeChangeRequest {
|
|
183
|
+
changeRequestId: string;
|
|
184
|
+
scopeType: KnowledgeScopeType;
|
|
185
|
+
scopeId: string;
|
|
186
|
+
resourceType: ChangeRequestResourceType;
|
|
187
|
+
operation: ChangeRequestOperation;
|
|
188
|
+
targetPath: string | null;
|
|
189
|
+
baseVersionId: string | null;
|
|
190
|
+
proposedContentType: string | null;
|
|
191
|
+
status: ChangeRequestStatus;
|
|
192
|
+
proposerId: string;
|
|
193
|
+
proposerKind: ChangeRequestActorKind;
|
|
194
|
+
rationale: string;
|
|
195
|
+
reviewerId: string | null;
|
|
196
|
+
reviewerKind: ChangeRequestActorKind | null;
|
|
197
|
+
reviewedAt: string | null;
|
|
198
|
+
reviewNote: string | null;
|
|
199
|
+
appliedRef: string | null;
|
|
200
|
+
createdAt: string;
|
|
201
|
+
updatedAt: string;
|
|
202
|
+
}
|
|
203
|
+
/** Per-type proposal payload for `proposeScopeChange`. */
|
|
204
|
+
interface ProposeScopeChangeInput {
|
|
205
|
+
resourceType: ChangeRequestResourceType;
|
|
206
|
+
operation: ChangeRequestOperation;
|
|
207
|
+
/** Why the change is proposed — shown to the reviewer. */
|
|
208
|
+
rationale: string;
|
|
209
|
+
/** doc: the path the proposal applies to (e.g. designs/data-center.md). */
|
|
210
|
+
targetPath?: string;
|
|
211
|
+
/** doc create/update: the staged body to upload (markdown or other text). */
|
|
212
|
+
content?: string;
|
|
213
|
+
/** doc create/update: content type of the staged body (default text/markdown). */
|
|
214
|
+
contentType?: string;
|
|
215
|
+
/** profile: the proposed value ({ about, description, links }). */
|
|
216
|
+
proposedValue?: unknown;
|
|
217
|
+
}
|
|
218
|
+
/** Voice settings — core agent config. Mirrors `VoiceConfig` in `@alfe/types`. */
|
|
219
|
+
interface AgentVoiceConfig {
|
|
220
|
+
/** ElevenLabs voice ID; platform default when unset. */
|
|
221
|
+
voiceId?: string;
|
|
222
|
+
ttsModel?: string;
|
|
223
|
+
enabled?: boolean;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The agent's own public identity, as returned by `updateSelf`, `generateAvatar`,
|
|
227
|
+
* `presignAvatar`'s finalize (`finalizeAvatar`). This is the public agent
|
|
228
|
+
* projection; only the identity-relevant fields are typed here — the response
|
|
229
|
+
* carries the full public agent record.
|
|
230
|
+
*/
|
|
231
|
+
interface AgentSelf {
|
|
232
|
+
agentId: string;
|
|
233
|
+
tenantId: string;
|
|
234
|
+
name: string;
|
|
235
|
+
avatarUrl?: string;
|
|
236
|
+
voiceConfig?: AgentVoiceConfig;
|
|
237
|
+
status: string;
|
|
238
|
+
}
|
|
239
|
+
/** Result of `presignAvatar` — the agent PUTs bytes to `uploadUrl`, then finalizes with `s3Key`. */
|
|
240
|
+
interface AgentAvatarPresign {
|
|
241
|
+
/** Presigned PUT URL to upload the image bytes to. */
|
|
242
|
+
uploadUrl: string;
|
|
243
|
+
/** Object key — echoed back to `finalizeAvatar`. */
|
|
244
|
+
s3Key: string;
|
|
245
|
+
/** Stable public URL the avatar will be served from once finalized. */
|
|
246
|
+
publicUrl: string;
|
|
247
|
+
/** ISO expiry of the presigned PUT URL. */
|
|
248
|
+
expiresAt: string;
|
|
249
|
+
}
|
|
250
|
+
/** A voice in the platform catalogue (ElevenLabs), from `listVoices`. */
|
|
251
|
+
interface AgentVoice {
|
|
252
|
+
id: string;
|
|
253
|
+
name: string;
|
|
254
|
+
previewUrl: string;
|
|
255
|
+
description: string;
|
|
256
|
+
labels: Record<string, string>;
|
|
257
|
+
category: string;
|
|
258
|
+
}
|
|
259
|
+
/** The ElevenLabs models with a pricing row — the TTS endpoint rejects any other value. */
|
|
260
|
+
type VoiceTtsModel = "eleven_turbo_v2_5" | "eleven_multilingual_v2";
|
|
261
|
+
interface VoiceTtsArgs {
|
|
262
|
+
/** Text to synthesize (1–5000 chars — the endpoint enforces this). */
|
|
263
|
+
text: string;
|
|
264
|
+
/** ElevenLabs voice id; platform default when unset. */
|
|
265
|
+
voiceId?: string;
|
|
266
|
+
/** TTS model; `eleven_turbo_v2_5` (lower latency) when unset. */
|
|
267
|
+
model?: VoiceTtsModel;
|
|
268
|
+
}
|
|
269
|
+
/** Raw synthesized audio plus its PCM framing (from the response headers). */
|
|
270
|
+
interface VoiceTtsResult {
|
|
271
|
+
/** Raw little-endian PCM samples — no container. Wrap in WAV to make a playable file. */
|
|
272
|
+
audio: Buffer;
|
|
273
|
+
/** Samples per second (e.g. 24000). */
|
|
274
|
+
sampleRate: number;
|
|
275
|
+
/** Channel count (mono = 1). */
|
|
276
|
+
channels: number;
|
|
277
|
+
/** Bits per sample (e.g. 16). */
|
|
278
|
+
bitDepth: number;
|
|
279
|
+
}
|
|
280
|
+
interface VoiceSttArgs {
|
|
281
|
+
/** Raw linear16 (16-bit little-endian) mono PCM samples — no WAV/container header. */
|
|
282
|
+
audio: Uint8Array;
|
|
283
|
+
/** Sample rate of `audio` in Hz (8000–48000). */
|
|
284
|
+
sampleRate: number;
|
|
285
|
+
}
|
|
286
|
+
interface VoiceSttResult {
|
|
287
|
+
text: string;
|
|
288
|
+
/** Deepgram confidence in (0,1]. */
|
|
289
|
+
confidence: number;
|
|
290
|
+
}
|
|
291
|
+
declare class AgentApiClient {
|
|
292
|
+
private readonly apiKey;
|
|
293
|
+
private readonly apiUrl;
|
|
294
|
+
constructor(config: AgentApiClientConfig);
|
|
295
|
+
private request;
|
|
296
|
+
syncRegister(args?: {
|
|
297
|
+
displayName?: string;
|
|
298
|
+
}): Promise<{
|
|
299
|
+
agent: SyncAgentInfo;
|
|
300
|
+
}>;
|
|
301
|
+
syncGetManifest(): Promise<SyncManifest>;
|
|
302
|
+
syncPresign(args: {
|
|
303
|
+
files: {
|
|
304
|
+
path: string;
|
|
305
|
+
operation: "put" | "get";
|
|
306
|
+
contentType?: string;
|
|
307
|
+
}[];
|
|
308
|
+
}): Promise<{
|
|
309
|
+
urls: SyncPresignedUrl[];
|
|
310
|
+
}>;
|
|
311
|
+
syncConfirmUpload(args: {
|
|
312
|
+
filePath: string;
|
|
313
|
+
hash: string;
|
|
314
|
+
size: number;
|
|
315
|
+
storageClass?: "STANDARD" | "GLACIER_IR";
|
|
316
|
+
}): Promise<SyncConfirmedUpload>;
|
|
317
|
+
syncReconstruct(args: {
|
|
318
|
+
mode: "full" | "active" | "memory";
|
|
319
|
+
}): Promise<SyncReconstructBundle>;
|
|
320
|
+
syncGetStats(): Promise<SyncAgentStats>;
|
|
321
|
+
syncListFiles(args?: {
|
|
322
|
+
prefix?: string;
|
|
323
|
+
}): Promise<{
|
|
324
|
+
files: SyncFileEntry[];
|
|
325
|
+
}>;
|
|
326
|
+
syncListSessions(): Promise<{
|
|
327
|
+
sessions: SyncSessionEntry[];
|
|
328
|
+
}>;
|
|
329
|
+
syncGetSession(sessionId: string): Promise<SyncSessionContent>;
|
|
330
|
+
syncDeleteFile(filePath: string): Promise<{
|
|
331
|
+
removed: boolean;
|
|
332
|
+
}>;
|
|
333
|
+
sharedListFiles(args: {
|
|
334
|
+
scope: "org" | "team" | "project";
|
|
335
|
+
scopeId: string;
|
|
336
|
+
}): Promise<{
|
|
337
|
+
files: SharedFileEntry[];
|
|
338
|
+
nextCursor: string | null;
|
|
339
|
+
}>;
|
|
340
|
+
sharedDownloadUrl(args: {
|
|
341
|
+
scope: "org" | "team" | "project";
|
|
342
|
+
scopeId: string;
|
|
343
|
+
filePath: string;
|
|
344
|
+
}): Promise<{
|
|
345
|
+
downloadUrl: string;
|
|
346
|
+
expiresIn: number;
|
|
347
|
+
}>;
|
|
348
|
+
listIntegrations(): Promise<IntegrationInstall[]>;
|
|
349
|
+
getIntegrationConfig(integrationId: string): Promise<IntegrationConfigResult>;
|
|
350
|
+
updateIntegrationConfig(integrationId: string, config: Record<string, unknown>): Promise<void>;
|
|
351
|
+
installIntegration(integrationId: string, options?: {
|
|
352
|
+
version?: string;
|
|
353
|
+
config?: Record<string, unknown>;
|
|
354
|
+
}): Promise<IntegrationInstall>;
|
|
355
|
+
removeIntegration(integrationId: string): Promise<IntegrationInstall>;
|
|
356
|
+
getOAuthUrl(provider: string, scopes?: string[]): Promise<{
|
|
357
|
+
url: string;
|
|
358
|
+
provider: string;
|
|
359
|
+
expiresIn: number;
|
|
360
|
+
}>;
|
|
361
|
+
getOAuthStatus(provider: string): Promise<{
|
|
362
|
+
provider: string;
|
|
363
|
+
connected: boolean;
|
|
364
|
+
config?: Record<string, string>;
|
|
365
|
+
}>;
|
|
366
|
+
getRegistry(): Promise<{
|
|
367
|
+
integrations: RegistryEntry[];
|
|
368
|
+
}>;
|
|
369
|
+
/**
|
|
370
|
+
* Returns every connected Google account for the agent. Multi-account by
|
|
371
|
+
* design — the openclaw-google plugin requires the LLM to pass `email`
|
|
372
|
+
* explicitly to `google_run_command` so an account is always selected
|
|
373
|
+
* deliberately.
|
|
374
|
+
*
|
|
375
|
+
* 2026-05-14 (connections-redesign PR 1): the legacy flat shape (`email`,
|
|
376
|
+
* `refreshToken`, `accessToken`, etc., populated from the default account)
|
|
377
|
+
* is gone. Iterate over `accounts`.
|
|
378
|
+
*/
|
|
379
|
+
getGoogleCredentials(): Promise<{
|
|
380
|
+
accounts: {
|
|
381
|
+
email: string;
|
|
382
|
+
refreshToken: string;
|
|
383
|
+
clientId: string;
|
|
384
|
+
clientSecret: string;
|
|
385
|
+
displayName?: string;
|
|
386
|
+
connectedAt?: string;
|
|
387
|
+
}[];
|
|
388
|
+
}>;
|
|
389
|
+
disconnectGoogleAccount(email: string): Promise<{
|
|
390
|
+
accounts: {
|
|
391
|
+
email: string;
|
|
392
|
+
displayName?: string;
|
|
393
|
+
connectedAt?: string;
|
|
394
|
+
}[];
|
|
395
|
+
}>;
|
|
396
|
+
getGoogleChatCredentials(): Promise<{
|
|
397
|
+
email: string;
|
|
398
|
+
refreshToken: string;
|
|
399
|
+
clientId: string;
|
|
400
|
+
clientSecret: string;
|
|
401
|
+
displayName?: string;
|
|
402
|
+
}>;
|
|
403
|
+
/**
|
|
404
|
+
* Fetch decrypted credentials for ONE specific connection by its
|
|
405
|
+
* stable connectionId (connection-scoped, vs the provider-scoped
|
|
406
|
+
* `get<Provider>Credentials` helpers). Used by the daemon to resolve
|
|
407
|
+
* a Custom Connection-driven integration's credentials from the
|
|
408
|
+
* exact connection it was installed from — every custom connection
|
|
409
|
+
* shares the `custom` provider id, so provider-scoping is ambiguous.
|
|
410
|
+
*
|
|
411
|
+
* For custom connections `accessToken` is the JSON-encoded secret
|
|
412
|
+
* bundle (the daemon un-bundles it); non-secret fields are on
|
|
413
|
+
* `providerMetadata`. The endpoint enforces that the connection is in
|
|
414
|
+
* the calling agent's effective scope (403 otherwise).
|
|
415
|
+
*/
|
|
416
|
+
getConnectionCredentials(connectionId: string): Promise<{
|
|
417
|
+
provider: string;
|
|
418
|
+
connectionId: string;
|
|
419
|
+
accountIdentifier?: string;
|
|
420
|
+
accessToken?: string;
|
|
421
|
+
providerMetadata?: Record<string, unknown>;
|
|
422
|
+
[key: string]: unknown;
|
|
423
|
+
}>;
|
|
424
|
+
/**
|
|
425
|
+
* Resolve the primary cTrader Connection's credentials for the calling
|
|
426
|
+
* agent. Unlike most providers, the cTrader Open API needs app-level auth
|
|
427
|
+
* (`clientId` + `clientSecret`) AND account auth (`accessToken` +
|
|
428
|
+
* `accountId`) on the socket, so `@alfe.ai/ctrader-mcp` self-fetches the
|
|
429
|
+
* full set here at startup (the atlassian/google pattern). `clientId` /
|
|
430
|
+
* `clientSecret` are the SST-sourced global app credentials the connect
|
|
431
|
+
* endpoint injects — they are never persisted on the connection. `host` is
|
|
432
|
+
* the resolved TLS endpoint (`live.ctraderapi.com` / `demo.ctraderapi.com`)
|
|
433
|
+
* derived from the selected account's live/demo flag.
|
|
434
|
+
*/
|
|
435
|
+
getCTraderCredentials(): Promise<{
|
|
436
|
+
accessToken: string;
|
|
437
|
+
refreshToken: string;
|
|
438
|
+
accountId: string;
|
|
439
|
+
host: string;
|
|
440
|
+
clientId: string;
|
|
441
|
+
clientSecret: string;
|
|
442
|
+
}>;
|
|
443
|
+
/**
|
|
444
|
+
* Pattern A: multi-account credential fetch for cTrader.
|
|
445
|
+
*
|
|
446
|
+
* Unlike atlassian/salesforce (one Connection row per account/site), a
|
|
447
|
+
* cTrader is MULTI-grant per agent: an agent may connect several distinct
|
|
448
|
+
* cTrader logins, each its own Connection row keyed on `accountIdentifier =
|
|
449
|
+
* ctid:<userId>` (Phase 1). This aggregates the *trading accounts* across
|
|
450
|
+
* ALL of those Connection rows — each row contributes its `availableAccounts`
|
|
451
|
+
* flattened, and every account carries ITS OWN grant's `accessToken` (the
|
|
452
|
+
* token that authenticates that account against the cTrader Open API). One
|
|
453
|
+
* OAuth grant still covers all accounts under that single login on one shared
|
|
454
|
+
* token; only the `ctidTraderAccountId` and the protobuf socket `host` (live
|
|
455
|
+
* vs demo) differ within a grant. Across grants the tokens differ, so the
|
|
456
|
+
* token is now PER-ACCOUNT rather than hoisted to the top level.
|
|
457
|
+
*
|
|
458
|
+
* `host` per account is derived from the account's `isLive` flag
|
|
459
|
+
* (`live.ctraderapi.com` / `demo.ctraderapi.com`) — the same mapping the
|
|
460
|
+
* connect provider applies server-side when an account is auto-selected.
|
|
461
|
+
*
|
|
462
|
+
* `clientId` / `clientSecret` are the SST-sourced GLOBAL app credentials the
|
|
463
|
+
* connect endpoint injects — identical across every Connection row (one
|
|
464
|
+
* cTrader app), never persisted on a connection. We take them from the first
|
|
465
|
+
* row that carries them.
|
|
466
|
+
*
|
|
467
|
+
* Accounts are deduped on `ctidTraderAccountId` first-wins: Spotware ids are
|
|
468
|
+
* globally unique across logins, so a duplicate can only appear if the same
|
|
469
|
+
* account somehow surfaced under two grants — first-wins keeps it
|
|
470
|
+
* deterministic.
|
|
471
|
+
*
|
|
472
|
+
* `accounts` may be empty (no cTrader Connection at all), in which case we
|
|
473
|
+
* return empty creds rather than throwing.
|
|
474
|
+
*/
|
|
475
|
+
getCTraderAccounts(): Promise<{
|
|
476
|
+
accounts: {
|
|
477
|
+
ctidTraderAccountId: string;
|
|
478
|
+
host: string;
|
|
479
|
+
isLive: boolean;
|
|
480
|
+
brokerName?: string;
|
|
481
|
+
accountNumber?: string;
|
|
482
|
+
accessToken: string;
|
|
483
|
+
}[];
|
|
484
|
+
clientId: string;
|
|
485
|
+
clientSecret: string;
|
|
486
|
+
}>;
|
|
487
|
+
/**
|
|
488
|
+
* @deprecated Returns a single primary credential blob (legacy "pick-the-
|
|
489
|
+
* default-connection" shape). Use `getGithubAccounts()` for the multi-
|
|
490
|
+
* account shape required by Pattern A — explicit selector args on every
|
|
491
|
+
* tool. Retained because the `@alfe.ai/github-mcp` proxy is the
|
|
492
|
+
* only consumer that knows about Pattern A; legacy env-interpolation
|
|
493
|
+
* callers will keep hitting `/credentials` until they move to the proxy.
|
|
494
|
+
*/
|
|
495
|
+
getGithubCredentials(): Promise<{
|
|
496
|
+
login: string;
|
|
497
|
+
accessToken: string;
|
|
498
|
+
}>;
|
|
499
|
+
/**
|
|
500
|
+
* Pattern A: multi-account credential fetch for GitHub.
|
|
501
|
+
*
|
|
502
|
+
* Returns every agent-scoped GitHub connection. The caller is expected
|
|
503
|
+
* to require a `login` selector on every credential-touching tool and
|
|
504
|
+
* look up the matching account at dispatch time.
|
|
505
|
+
*
|
|
506
|
+
* GitHub OAuth tokens have no expiry (`tokenLifecycle: "no_expiry"`),
|
|
507
|
+
* so there is intentionally no `refreshGithubAccountToken` method — if
|
|
508
|
+
* a token is revoked the user must re-run the OAuth flow.
|
|
509
|
+
*
|
|
510
|
+
* Returned `accounts[i].login` is the GitHub username — the stable
|
|
511
|
+
* cross-session identifier the LLM should pass.
|
|
512
|
+
*/
|
|
513
|
+
getGithubAccounts(): Promise<{
|
|
514
|
+
accounts: {
|
|
515
|
+
connectionId: string;
|
|
516
|
+
accountIdentifier: string;
|
|
517
|
+
displayName: string | null;
|
|
518
|
+
connectedAt: string;
|
|
519
|
+
accessToken: string;
|
|
520
|
+
login: string;
|
|
521
|
+
scopes: string;
|
|
522
|
+
}[];
|
|
523
|
+
}>;
|
|
524
|
+
/**
|
|
525
|
+
* @deprecated Returns a single primary credential blob (legacy "pick-the-
|
|
526
|
+
* default-connection" shape). Use `getXeroAccounts()` for the multi-
|
|
527
|
+
* account shape required by Pattern A — explicit selector args on every
|
|
528
|
+
* tool. This method will be removed once all consumers migrate.
|
|
529
|
+
*/
|
|
530
|
+
getXeroCredentials(): Promise<{
|
|
531
|
+
accessToken: string;
|
|
532
|
+
accessTokenExpiresAt: string;
|
|
533
|
+
xeroTenantId: string;
|
|
534
|
+
}>;
|
|
535
|
+
/**
|
|
536
|
+
* Pattern A: multi-account credential fetch for Xero. Returns every
|
|
537
|
+
* agent-scoped Xero connection. The caller is expected to require a
|
|
538
|
+
* selector arg (e.g. `xeroTenantId`) on every credential-touching tool
|
|
539
|
+
* and look up the matching account by that selector at dispatch time.
|
|
540
|
+
*
|
|
541
|
+
* Returned `accounts[i].accountIdentifier` is the Xero tenantId — the
|
|
542
|
+
* stable cross-session identifier the LLM should pass.
|
|
543
|
+
*/
|
|
544
|
+
getXeroAccounts(): Promise<{
|
|
545
|
+
accounts: {
|
|
546
|
+
connectionId: string;
|
|
547
|
+
accountIdentifier: string;
|
|
548
|
+
displayName: string | null;
|
|
549
|
+
connectedAt: string;
|
|
550
|
+
accessToken: string;
|
|
551
|
+
accessTokenExpiresAt: string;
|
|
552
|
+
xeroTenantId: string;
|
|
553
|
+
}[];
|
|
554
|
+
}>;
|
|
555
|
+
refreshXeroToken(): Promise<{
|
|
556
|
+
accessToken: string;
|
|
557
|
+
expiresAt: string;
|
|
558
|
+
}>;
|
|
559
|
+
/**
|
|
560
|
+
* Pattern A: refresh a specific Xero connection by its `accountIdentifier`
|
|
561
|
+
* (the Xero `tenantId`). The legacy `refreshXeroToken()` only refreshes
|
|
562
|
+
* the *primary* connection, which is wrong for multi-tenant Xero where
|
|
563
|
+
* each tenant has its own non-interchangeable access token.
|
|
564
|
+
*/
|
|
565
|
+
refreshXeroAccountToken(xeroTenantId: string): Promise<{
|
|
566
|
+
accessToken: string;
|
|
567
|
+
accessTokenExpiresAt: string;
|
|
568
|
+
expiresAt: string;
|
|
569
|
+
}>;
|
|
570
|
+
/**
|
|
571
|
+
* @deprecated Returns a single primary credential blob (legacy "pick-the-
|
|
572
|
+
* default-connection" shape). Use `getNotionAccounts()` for the multi-
|
|
573
|
+
* account shape required by Pattern A.
|
|
574
|
+
*/
|
|
575
|
+
getNotionCredentials(): Promise<{
|
|
576
|
+
accessToken: string;
|
|
577
|
+
workspaceId: string;
|
|
578
|
+
workspaceName: string;
|
|
579
|
+
}>;
|
|
580
|
+
/**
|
|
581
|
+
* Pattern A: multi-account credential fetch for Notion. Returns every
|
|
582
|
+
* agent-scoped Notion connection. The caller is expected to require a
|
|
583
|
+
* selector arg (e.g. `workspaceId`) on every credential-touching tool.
|
|
584
|
+
*
|
|
585
|
+
* Returned `accounts[i].accountIdentifier` is the Notion workspaceId.
|
|
586
|
+
*/
|
|
587
|
+
getNotionAccounts(): Promise<{
|
|
588
|
+
accounts: {
|
|
589
|
+
connectionId: string;
|
|
590
|
+
accountIdentifier: string;
|
|
591
|
+
displayName: string | null;
|
|
592
|
+
connectedAt: string;
|
|
593
|
+
accessToken: string;
|
|
594
|
+
workspaceId: string;
|
|
595
|
+
workspaceName: string;
|
|
596
|
+
}[];
|
|
597
|
+
}>;
|
|
598
|
+
/**
|
|
599
|
+
* @deprecated Returns a single primary Atlassian Connection's credentials
|
|
600
|
+
* (one OAuth user, one cloudId) — the legacy "pick-the-default-connection"
|
|
601
|
+
* shape. Atlassian is multi-site by nature (each OAuth user may have
|
|
602
|
+
* access to multiple Cloud sites), so Pattern A plugins MUST use
|
|
603
|
+
* `getAtlassianAccounts()` to discover the full set and dispatch via
|
|
604
|
+
* the `cloudId` selector arg.
|
|
605
|
+
*/
|
|
606
|
+
getAtlassianCredentials(): Promise<{
|
|
607
|
+
accessToken: string;
|
|
608
|
+
refreshToken: string;
|
|
609
|
+
accessTokenExpiresAt: string;
|
|
610
|
+
cloudId: string;
|
|
611
|
+
siteName: string;
|
|
612
|
+
siteUrl: string;
|
|
613
|
+
email: string;
|
|
614
|
+
enabledProducts: string[];
|
|
615
|
+
clientId: string;
|
|
616
|
+
clientSecret: string;
|
|
617
|
+
}>;
|
|
618
|
+
refreshAtlassianToken(): Promise<{
|
|
619
|
+
accessToken: string;
|
|
620
|
+
expiresAt: string;
|
|
621
|
+
}>;
|
|
622
|
+
/**
|
|
623
|
+
* Pattern A: multi-account / multi-site credential fetch for Atlassian.
|
|
624
|
+
*
|
|
625
|
+
* Returns every agent-scoped Atlassian Connection. Each Connection is
|
|
626
|
+
* one OAuth user with a single access token and N accessible Cloud
|
|
627
|
+
* sites (`availableSites`). The caller is expected to:
|
|
628
|
+
*
|
|
629
|
+
* 1. Flatten (connection × cloudId) into one MCP child per site.
|
|
630
|
+
* 2. Require a `cloudId` selector on every credential-touching tool.
|
|
631
|
+
* 3. Use the access token bound to the Connection that owns the
|
|
632
|
+
* requested `cloudId` (Atlassian shares one access token across
|
|
633
|
+
* all sites accessible to the OAuth user).
|
|
634
|
+
*
|
|
635
|
+
* Per-account token refresh uses `refreshAtlassianAccountToken(email)`
|
|
636
|
+
* — refreshing one Connection rotates its single access token, which
|
|
637
|
+
* then applies to every cloudId for that Connection.
|
|
638
|
+
*
|
|
639
|
+
* Returned `accounts[i].accountIdentifier` is the OAuth user's email
|
|
640
|
+
* — the stable cross-session identifier for refresh purposes. The LLM
|
|
641
|
+
* never sees this directly: it picks a site via the `cloudId` arg
|
|
642
|
+
* instead.
|
|
643
|
+
*/
|
|
644
|
+
getAtlassianAccounts(): Promise<{
|
|
645
|
+
accounts: {
|
|
646
|
+
connectionId: string;
|
|
647
|
+
accountIdentifier: string;
|
|
648
|
+
displayName: string | null;
|
|
649
|
+
connectedAt: string;
|
|
650
|
+
accessToken: string;
|
|
651
|
+
accessTokenExpiresAt: string;
|
|
652
|
+
clientId: string;
|
|
653
|
+
clientSecret: string;
|
|
654
|
+
cloudId: string;
|
|
655
|
+
siteName: string;
|
|
656
|
+
siteUrl: string;
|
|
657
|
+
availableSites: {
|
|
658
|
+
id: string;
|
|
659
|
+
url: string;
|
|
660
|
+
name: string;
|
|
661
|
+
scopes?: string[];
|
|
662
|
+
avatarUrl?: string;
|
|
663
|
+
}[];
|
|
664
|
+
}[];
|
|
665
|
+
}>;
|
|
666
|
+
/**
|
|
667
|
+
* Pattern A: refresh a specific Atlassian Connection by `accountIdentifier`
|
|
668
|
+
* (the OAuth user's email).
|
|
669
|
+
*
|
|
670
|
+
* Atlassian rotates refresh tokens (`rotatesRefreshToken: true`); the
|
|
671
|
+
* server-side per-account refresh endpoint handles rotation and
|
|
672
|
+
* persistence. Refreshing one Connection updates its single access
|
|
673
|
+
* token, which applies to every accessible Cloud site (cloudId) for
|
|
674
|
+
* that OAuth user.
|
|
675
|
+
*
|
|
676
|
+
* Returns the new access token + expiry. The proxy is responsible for
|
|
677
|
+
* fanning the new token out to every child server it spawned for
|
|
678
|
+
* cloudIds owned by this Connection.
|
|
679
|
+
*/
|
|
680
|
+
refreshAtlassianAccountToken(accountIdentifier: string): Promise<{
|
|
681
|
+
accessToken: string;
|
|
682
|
+
accessTokenExpiresAt: string;
|
|
683
|
+
expiresAt: string;
|
|
684
|
+
}>;
|
|
685
|
+
/**
|
|
686
|
+
* @deprecated Returns a single primary credential blob (legacy "pick-the-
|
|
687
|
+
* default-connection" shape). Use `getMYOBAccounts()` for the multi-
|
|
688
|
+
* account shape required by Pattern A.
|
|
689
|
+
*/
|
|
690
|
+
getMYOBCredentials(): Promise<{
|
|
691
|
+
accessToken: string;
|
|
692
|
+
accessTokenExpiresAt: string;
|
|
693
|
+
myobBusinessId: string;
|
|
694
|
+
clientId: string;
|
|
695
|
+
}>;
|
|
696
|
+
/**
|
|
697
|
+
* Pattern A: multi-account credential fetch for MYOB. Returns every
|
|
698
|
+
* agent-scoped MYOB connection. The caller is expected to require a
|
|
699
|
+
* selector arg (e.g. `myobBusinessId` / `accountIdentifier`) on every
|
|
700
|
+
* credential-touching tool.
|
|
701
|
+
*
|
|
702
|
+
* Returned `accounts[i].accountIdentifier` is the MYOB businessId.
|
|
703
|
+
*/
|
|
704
|
+
getMYOBAccounts(): Promise<{
|
|
705
|
+
accounts: {
|
|
706
|
+
connectionId: string;
|
|
707
|
+
accountIdentifier: string;
|
|
708
|
+
displayName: string | null;
|
|
709
|
+
connectedAt: string;
|
|
710
|
+
accessToken: string;
|
|
711
|
+
accessTokenExpiresAt: string;
|
|
712
|
+
myobBusinessId: string;
|
|
713
|
+
clientId: string;
|
|
714
|
+
}[];
|
|
715
|
+
}>;
|
|
716
|
+
refreshMYOBToken(): Promise<{
|
|
717
|
+
accessToken: string;
|
|
718
|
+
expiresAt: string;
|
|
719
|
+
}>;
|
|
720
|
+
/**
|
|
721
|
+
* @deprecated Returns a single primary credential blob. Use
|
|
722
|
+
* `getSalesforceAccounts()` for the multi-account shape required by
|
|
723
|
+
* Pattern A.
|
|
724
|
+
*/
|
|
725
|
+
getSalesforceCredentials(): Promise<{
|
|
726
|
+
accessToken: string;
|
|
727
|
+
accessTokenExpiresAt: string;
|
|
728
|
+
instanceUrl: string;
|
|
729
|
+
orgId: string;
|
|
730
|
+
}>;
|
|
731
|
+
/**
|
|
732
|
+
* Pattern A: multi-account credential fetch for Salesforce. Returns every
|
|
733
|
+
* agent-scoped Salesforce connection. One OAuth grant maps to one org, so
|
|
734
|
+
* `accounts[i].accountIdentifier` (and `orgId`) is the Salesforce org id —
|
|
735
|
+
* the selector every credential-touching tool requires.
|
|
736
|
+
*/
|
|
737
|
+
getSalesforceAccounts(): Promise<{
|
|
738
|
+
accounts: {
|
|
739
|
+
connectionId: string;
|
|
740
|
+
accountIdentifier: string;
|
|
741
|
+
displayName: string | null;
|
|
742
|
+
connectedAt: string;
|
|
743
|
+
accessToken: string;
|
|
744
|
+
accessTokenExpiresAt: string;
|
|
745
|
+
instanceUrl: string;
|
|
746
|
+
orgId: string;
|
|
747
|
+
}[];
|
|
748
|
+
}>;
|
|
749
|
+
/**
|
|
750
|
+
* Refresh the access token for a specific Salesforce org. Salesforce
|
|
751
|
+
* tokens aren't interchangeable across orgs, so the connection is targeted
|
|
752
|
+
* by `accountIdentifier` (the org id) — mirrors `refreshXeroAccountToken`.
|
|
753
|
+
*/
|
|
754
|
+
refreshSalesforceAccountToken(orgId: string): Promise<{
|
|
755
|
+
accessToken: string;
|
|
756
|
+
accessTokenExpiresAt: string;
|
|
757
|
+
expiresAt: string;
|
|
758
|
+
}>;
|
|
759
|
+
/**
|
|
760
|
+
* Disconnects one connected Microsoft 365 account for the agent, by its
|
|
761
|
+
* `accountIdentifier`. Hits the generic per-account disconnect route
|
|
762
|
+
* (`DELETE /agent/connect/microsoft/accounts/{accountIdentifier}`), which
|
|
763
|
+
* resolves across the agent's full effective scope chain and deletes the
|
|
764
|
+
* matching Connection row. Returns the remaining accounts.
|
|
765
|
+
*
|
|
766
|
+
* IMPORTANT: pass the `accountIdentifier` from `getMicrosoftAccounts()`, NOT
|
|
767
|
+
* a synthesised email. For Microsoft, `accountIdentifier` is the user's email
|
|
768
|
+
* only when the Graph profile fetch succeeded at connect time; it falls back
|
|
769
|
+
* to the Azure tenant id (`tid` claim) otherwise. The backend matches on
|
|
770
|
+
* `accountIdentifier` exactly, so passing an email would 404 on those
|
|
771
|
+
* fallback-identifier accounts. (This is why the param is not named `email`,
|
|
772
|
+
* unlike `disconnectGoogleAccount` where the identifier is always the email.)
|
|
773
|
+
*/
|
|
774
|
+
disconnectMicrosoftAccount(accountIdentifier: string): Promise<{
|
|
775
|
+
accounts: {
|
|
776
|
+
accountIdentifier: string;
|
|
777
|
+
displayName?: string;
|
|
778
|
+
connectedAt?: string;
|
|
779
|
+
}[];
|
|
780
|
+
}>;
|
|
781
|
+
/**
|
|
782
|
+
* Pattern A: multi-account credential fetch for Microsoft 365.
|
|
783
|
+
*
|
|
784
|
+
* Returns every agent-scoped Microsoft connection. The caller is expected
|
|
785
|
+
* to require an `email` selector on every credential-touching tool and
|
|
786
|
+
* look up the matching account at dispatch time.
|
|
787
|
+
*
|
|
788
|
+
* Returned `accounts[i].accountIdentifier` is the user's primary email
|
|
789
|
+
* (or the tid claim as fallback) — the stable cross-session identifier
|
|
790
|
+
* the LLM should pass.
|
|
791
|
+
*
|
|
792
|
+
* Per-account token refresh is exposed via `refreshMicrosoftAccountToken`,
|
|
793
|
+
* NOT `refreshXeroAccountToken` — Microsoft refresh tokens are not
|
|
794
|
+
* interchangeable across (tenant, user) pairs.
|
|
795
|
+
*/
|
|
796
|
+
getMicrosoftAccounts(): Promise<{
|
|
797
|
+
accounts: {
|
|
798
|
+
connectionId: string;
|
|
799
|
+
accountIdentifier: string;
|
|
800
|
+
displayName: string | null;
|
|
801
|
+
connectedAt: string;
|
|
802
|
+
accessToken: string;
|
|
803
|
+
accessTokenExpiresAt: string;
|
|
804
|
+
refreshToken: string;
|
|
805
|
+
clientId: string;
|
|
806
|
+
clientSecret: string;
|
|
807
|
+
email: string;
|
|
808
|
+
microsoftTenantId: string;
|
|
809
|
+
workspaceDomain: string;
|
|
810
|
+
}[];
|
|
811
|
+
}>;
|
|
812
|
+
/**
|
|
813
|
+
* Pattern A: refresh a specific Microsoft 365 connection by its
|
|
814
|
+
* `accountIdentifier`. For Microsoft, `accountIdentifier` is the user's
|
|
815
|
+
* email when the Graph profile fetch succeeded at connect time, and the
|
|
816
|
+
* Azure tenant id (`tid` claim) as fallback. Callers should pass the
|
|
817
|
+
* value returned by `getMicrosoftAccounts()` rather than synthesising
|
|
818
|
+
* an email locally.
|
|
819
|
+
*
|
|
820
|
+
* Microsoft refresh tokens are bound to a specific (tenant, user) pair —
|
|
821
|
+
* they are NOT interchangeable across accounts, so per-account refresh
|
|
822
|
+
* is mandatory. The generic /accounts/{accountIdentifier}/refresh
|
|
823
|
+
* endpoint walks the agent's full visible scope chain to find a matching
|
|
824
|
+
* connection (works for inherited team/project Microsoft connections).
|
|
825
|
+
*/
|
|
826
|
+
refreshMicrosoftAccountToken(accountIdentifier: string): Promise<{
|
|
827
|
+
accessToken: string;
|
|
828
|
+
accessTokenExpiresAt: string;
|
|
829
|
+
expiresAt: string;
|
|
830
|
+
}>;
|
|
831
|
+
getTeamsCredentials(): Promise<{
|
|
832
|
+
agentId: string;
|
|
833
|
+
tenantId: string;
|
|
834
|
+
azureAppId: string;
|
|
835
|
+
azureBotId: string;
|
|
836
|
+
azureClientSecret: string;
|
|
837
|
+
botDisplayName?: string;
|
|
838
|
+
teamsTenantId?: string;
|
|
839
|
+
serviceUrl?: string;
|
|
840
|
+
}>;
|
|
841
|
+
sendTeamsMessage(data: {
|
|
842
|
+
conversationId: string;
|
|
843
|
+
text?: string;
|
|
844
|
+
adaptiveCard?: Record<string, unknown>;
|
|
845
|
+
}): Promise<{
|
|
846
|
+
ok: boolean;
|
|
847
|
+
activityId: string;
|
|
848
|
+
}>;
|
|
849
|
+
listTeamsChannels(): Promise<{
|
|
850
|
+
channels: {
|
|
851
|
+
id: string;
|
|
852
|
+
name: string;
|
|
853
|
+
description?: string;
|
|
854
|
+
}[];
|
|
855
|
+
}>;
|
|
856
|
+
presignAttachments(files: {
|
|
857
|
+
filename: string;
|
|
858
|
+
mimeType: string;
|
|
859
|
+
size: number;
|
|
860
|
+
}[]): Promise<{
|
|
861
|
+
attachments: {
|
|
862
|
+
id: string;
|
|
863
|
+
uploadUrl: string;
|
|
864
|
+
downloadUrl: string;
|
|
865
|
+
s3Key: string;
|
|
866
|
+
expiresAt: string;
|
|
867
|
+
}[];
|
|
868
|
+
}>;
|
|
869
|
+
/**
|
|
870
|
+
* Generate an image from a text prompt and get back a STABLE, public URL
|
|
871
|
+
* (served from the agent-assets CDN — it does not expire). Embed the returned
|
|
872
|
+
* `imageUrl` in a reply as markdown to show it to the user.
|
|
873
|
+
*
|
|
874
|
+
* ASYNC: `gpt-image-1` routinely runs 30–60s, which exceeds the API Gateway
|
|
875
|
+
* 30s ceiling, so this enqueues a job (`POST /agent/images/generate` →
|
|
876
|
+
* `jobId`) then polls (`GET /agent/images/{jobId}`) until it completes. The
|
|
877
|
+
* worker's real failure message (e.g. an unsupported `size`) surfaces via the
|
|
878
|
+
* job's `error` field.
|
|
879
|
+
*/
|
|
880
|
+
generateImage(args: {
|
|
881
|
+
prompt: string;
|
|
882
|
+
model?: string;
|
|
883
|
+
size?: string;
|
|
884
|
+
quality?: string;
|
|
885
|
+
}): Promise<{
|
|
886
|
+
imageUrl: string;
|
|
887
|
+
model: string;
|
|
888
|
+
}>;
|
|
889
|
+
recordActivity(data: {
|
|
890
|
+
userId?: string;
|
|
891
|
+
channel: string;
|
|
892
|
+
role: "user" | "assistant";
|
|
893
|
+
}): Promise<{
|
|
894
|
+
recorded: boolean;
|
|
895
|
+
}>;
|
|
896
|
+
/** Update the agent's own name and/or voice config. Returns the updated agent. */
|
|
897
|
+
updateSelf(update: {
|
|
898
|
+
name?: string;
|
|
899
|
+
voiceConfig?: AgentVoiceConfig;
|
|
900
|
+
}): Promise<AgentSelf>;
|
|
901
|
+
/**
|
|
902
|
+
* Generate the agent's own avatar from a text prompt. The image is generated,
|
|
903
|
+
* stored, and set on the agent server-side; returns the updated agent.
|
|
904
|
+
*
|
|
905
|
+
* ASYNC (same reason as `generateImage`): avatar gen runs `gpt-image-1`
|
|
906
|
+
* (30–60s) which exceeds the API Gateway 30s ceiling, so this enqueues a job
|
|
907
|
+
* (`POST /agent/avatar/generate` → `jobId`) then polls (`GET /agent/avatar/{jobId}`)
|
|
908
|
+
* until the avatar is set. Signature unchanged — the plugin is unaffected.
|
|
909
|
+
*/
|
|
910
|
+
generateAvatar(args: {
|
|
911
|
+
prompt: string;
|
|
912
|
+
}): Promise<AgentSelf>;
|
|
913
|
+
/**
|
|
914
|
+
* Get a presigned PUT URL to upload a new avatar image. Upload the bytes to
|
|
915
|
+
* `uploadUrl`, then call `finalizeAvatar(s3Key)` to set it on the agent.
|
|
916
|
+
*/
|
|
917
|
+
presignAvatar(args: {
|
|
918
|
+
mimeType: string;
|
|
919
|
+
size: number;
|
|
920
|
+
}): Promise<AgentAvatarPresign>;
|
|
921
|
+
/**
|
|
922
|
+
* Finalize an avatar upload — validates ownership + size, then sets the
|
|
923
|
+
* agent's `avatarUrl` server-side. Returns the updated agent.
|
|
924
|
+
*/
|
|
925
|
+
finalizeAvatar(s3Key: string): Promise<AgentSelf>;
|
|
926
|
+
/** List the platform voice catalogue (ElevenLabs) so the agent can pick its own voice. */
|
|
927
|
+
listVoices(): Promise<{
|
|
928
|
+
voices: AgentVoice[];
|
|
929
|
+
}>;
|
|
930
|
+
/**
|
|
931
|
+
* Mint a fresh AES-256 data key for a specific (secret, field) pair. The
|
|
932
|
+
* encryption context is rebuilt server-side from `auth.tenantId` + the body
|
|
933
|
+
* fields including `fieldKey`; the agent cannot forge context for a scope
|
|
934
|
+
* or field it doesn't own. Legacy single-envelope secrets are migrated to
|
|
935
|
+
* `field#value` rows by the data migration, so call with `fieldKey: "value"`
|
|
936
|
+
* to reach them.
|
|
937
|
+
*/
|
|
938
|
+
generateSecretDataKey(args: {
|
|
939
|
+
scope: SecretScope;
|
|
940
|
+
scopeId: string;
|
|
941
|
+
secretId: string;
|
|
942
|
+
fieldKey: string;
|
|
943
|
+
}): Promise<GeneratedDataKey>;
|
|
944
|
+
/**
|
|
945
|
+
* Unwrap a wrapped data key so the agent can decrypt the envelope locally.
|
|
946
|
+
* `fieldKey` MUST match the value supplied when the data key was generated
|
|
947
|
+
* (it's bound into KMS encryption context); mismatch fails with
|
|
948
|
+
* `InvalidCiphertextException`.
|
|
949
|
+
*/
|
|
950
|
+
decryptSecretDataKey(args: {
|
|
951
|
+
scope: SecretScope;
|
|
952
|
+
scopeId: string;
|
|
953
|
+
secretId: string;
|
|
954
|
+
fieldKey: string;
|
|
955
|
+
dataKeyCiphertext: string;
|
|
956
|
+
}): Promise<{
|
|
957
|
+
plaintextKey: string;
|
|
958
|
+
}>;
|
|
959
|
+
/**
|
|
960
|
+
* Create a new secret with one or more fields. Encrypted fields must arrive
|
|
961
|
+
* pre-sealed (the agent has already obtained per-field data keys via
|
|
962
|
+
* `generateSecretDataKey({ ..., fieldKey })` and AES-encrypted locally).
|
|
963
|
+
* Plaintext fields ship the value inline.
|
|
964
|
+
*/
|
|
965
|
+
createSecret(args: {
|
|
966
|
+
scope: SecretScope;
|
|
967
|
+
scopeId: string;
|
|
968
|
+
secretId: string;
|
|
969
|
+
secretName: string;
|
|
970
|
+
category?: SecretCategory;
|
|
971
|
+
description?: string;
|
|
972
|
+
tags?: string[];
|
|
973
|
+
fields: {
|
|
974
|
+
key: string;
|
|
975
|
+
format?: FieldFormat;
|
|
976
|
+
sensitivity: FieldSensitivity;
|
|
977
|
+
value?: string;
|
|
978
|
+
envelope?: EncryptedEnvelopeV1;
|
|
979
|
+
}[];
|
|
980
|
+
reason?: string;
|
|
981
|
+
}): Promise<SecretAggregate>;
|
|
982
|
+
/** Fetch the secret aggregate plus per-field encrypted envelopes. */
|
|
983
|
+
getSecret(args: {
|
|
984
|
+
scope: SecretScope;
|
|
985
|
+
scopeId: string;
|
|
986
|
+
secretId: string;
|
|
987
|
+
}): Promise<{
|
|
988
|
+
aggregate: SecretAggregate;
|
|
989
|
+
envelopes: FieldEnvelope[];
|
|
990
|
+
}>;
|
|
991
|
+
/** Fetch one field. Plaintext: value inline. Encrypted: envelope. */
|
|
992
|
+
getSecretField(args: {
|
|
993
|
+
scope: SecretScope;
|
|
994
|
+
scopeId: string;
|
|
995
|
+
secretId: string;
|
|
996
|
+
fieldKey: string;
|
|
997
|
+
}): Promise<{
|
|
998
|
+
key: string;
|
|
999
|
+
sensitivity: FieldSensitivity;
|
|
1000
|
+
format?: FieldFormat;
|
|
1001
|
+
value?: string;
|
|
1002
|
+
envelope?: EncryptedEnvelopeV1;
|
|
1003
|
+
rotatedAt?: string;
|
|
1004
|
+
createdAt: string;
|
|
1005
|
+
updatedAt: string;
|
|
1006
|
+
}>;
|
|
1007
|
+
/** Add OR rotate one field. */
|
|
1008
|
+
setSecretField(args: {
|
|
1009
|
+
scope: SecretScope;
|
|
1010
|
+
scopeId: string;
|
|
1011
|
+
secretId: string;
|
|
1012
|
+
fieldKey: string;
|
|
1013
|
+
sensitivity: FieldSensitivity;
|
|
1014
|
+
format?: FieldFormat;
|
|
1015
|
+
value?: string;
|
|
1016
|
+
envelope?: EncryptedEnvelopeV1;
|
|
1017
|
+
reason?: string;
|
|
1018
|
+
}): Promise<{
|
|
1019
|
+
fieldKey: string;
|
|
1020
|
+
rotated: boolean;
|
|
1021
|
+
}>;
|
|
1022
|
+
/** Remove one field. */
|
|
1023
|
+
removeSecretField(args: {
|
|
1024
|
+
scope: SecretScope;
|
|
1025
|
+
scopeId: string;
|
|
1026
|
+
secretId: string;
|
|
1027
|
+
fieldKey: string;
|
|
1028
|
+
}): Promise<void>;
|
|
1029
|
+
/** Update secret-level metadata (name/description/tags/category). */
|
|
1030
|
+
updateSecretMetadata(args: {
|
|
1031
|
+
scope: SecretScope;
|
|
1032
|
+
scopeId: string;
|
|
1033
|
+
secretId: string;
|
|
1034
|
+
secretName?: string;
|
|
1035
|
+
description?: string;
|
|
1036
|
+
tags?: string[];
|
|
1037
|
+
category?: SecretCategory;
|
|
1038
|
+
reason?: string;
|
|
1039
|
+
}): Promise<SecretAggregate>;
|
|
1040
|
+
/** List metadata for secrets in a scope. Optional filters route through the byFacet GSI. */
|
|
1041
|
+
listSecrets(args: {
|
|
1042
|
+
scope: SecretScope;
|
|
1043
|
+
scopeId: string;
|
|
1044
|
+
category?: SecretCategory;
|
|
1045
|
+
tag?: string;
|
|
1046
|
+
fieldKey?: string;
|
|
1047
|
+
}): Promise<SecretMetadata[]>;
|
|
1048
|
+
/** Bounded changelog read — metadata-only audit entries. */
|
|
1049
|
+
getSecretHistory(args: {
|
|
1050
|
+
scope: SecretScope;
|
|
1051
|
+
scopeId: string;
|
|
1052
|
+
secretId: string;
|
|
1053
|
+
limit?: number;
|
|
1054
|
+
cursor?: string;
|
|
1055
|
+
}): Promise<{
|
|
1056
|
+
entries: ChangelogEntry[];
|
|
1057
|
+
nextCursor?: string;
|
|
1058
|
+
}>;
|
|
1059
|
+
/** Delete a secret (and all its field rows + tag rows + changelog rows). */
|
|
1060
|
+
deleteSecret(args: {
|
|
1061
|
+
scope: SecretScope;
|
|
1062
|
+
scopeId: string;
|
|
1063
|
+
secretId: string;
|
|
1064
|
+
}): Promise<void>;
|
|
1065
|
+
/** Enumerate scopes (org/team/project/agent) this agent can access. */
|
|
1066
|
+
listSecretScopes(): Promise<ScopeInfo[]>;
|
|
1067
|
+
/**
|
|
1068
|
+
* Returns the calling agent's own identity context — `{ agentId, tenantId }`
|
|
1069
|
+
* decoded server-side from the agent API token. Used by the
|
|
1070
|
+
* `@alfe.ai/openclaw-identity` plugin to bootstrap context when the
|
|
1071
|
+
* OpenClaw daemon doesn't plumb `ctx.agentId` through to plugin hooks.
|
|
1072
|
+
* Plugins should cache this for the daemon's lifetime (single-agent-per-
|
|
1073
|
+
* process invariant). One HTTP round-trip per process activate; not for
|
|
1074
|
+
* per-call use.
|
|
1075
|
+
*/
|
|
1076
|
+
whoami(): Promise<{
|
|
1077
|
+
agentId: string;
|
|
1078
|
+
tenantId: string;
|
|
1079
|
+
}>;
|
|
1080
|
+
resolveIdentity(args: {
|
|
1081
|
+
provider: string;
|
|
1082
|
+
platformId: string;
|
|
1083
|
+
kind?: "user" | "agent" | "service" | "bot" | "workspace";
|
|
1084
|
+
displayName?: string;
|
|
1085
|
+
}): Promise<{
|
|
1086
|
+
identityId: string | null;
|
|
1087
|
+
status: string;
|
|
1088
|
+
created?: boolean;
|
|
1089
|
+
reason?: string;
|
|
1090
|
+
/**
|
|
1091
|
+
* Flattened auriclabs permission strings for the resolved identity
|
|
1092
|
+
* (scope-prefixed where applicable). Empty array on miss / org service
|
|
1093
|
+
* outage — the runtime gate fails closed in that case.
|
|
1094
|
+
*/
|
|
1095
|
+
permissions: string[];
|
|
1096
|
+
}>;
|
|
1097
|
+
searchIdentities(args?: {
|
|
1098
|
+
q?: string;
|
|
1099
|
+
status?: string;
|
|
1100
|
+
limit?: number;
|
|
1101
|
+
}): Promise<{
|
|
1102
|
+
identities: unknown[];
|
|
1103
|
+
}>;
|
|
1104
|
+
getIdentityContext(identityId: string): Promise<{
|
|
1105
|
+
context: unknown;
|
|
1106
|
+
}>;
|
|
1107
|
+
mergeIdentities(survivorId: string, args: {
|
|
1108
|
+
mergedId: string;
|
|
1109
|
+
changedBy: {
|
|
1110
|
+
type: string;
|
|
1111
|
+
id: string;
|
|
1112
|
+
name?: string;
|
|
1113
|
+
};
|
|
1114
|
+
}): Promise<{
|
|
1115
|
+
ok: boolean;
|
|
1116
|
+
error?: string;
|
|
1117
|
+
}>;
|
|
1118
|
+
unmergeIdentity(identityId: string, args: {
|
|
1119
|
+
changedBy: {
|
|
1120
|
+
type: string;
|
|
1121
|
+
id: string;
|
|
1122
|
+
name?: string;
|
|
1123
|
+
};
|
|
1124
|
+
}): Promise<{
|
|
1125
|
+
ok: boolean;
|
|
1126
|
+
error?: string;
|
|
1127
|
+
}>;
|
|
1128
|
+
addIdentityNote(identityId: string, args: {
|
|
1129
|
+
content: string;
|
|
1130
|
+
category?: string;
|
|
1131
|
+
changedBy: {
|
|
1132
|
+
type: string;
|
|
1133
|
+
id: string;
|
|
1134
|
+
name?: string;
|
|
1135
|
+
};
|
|
1136
|
+
}): Promise<{
|
|
1137
|
+
noteId: string | null;
|
|
1138
|
+
}>;
|
|
1139
|
+
tagIdentity(identityId: string, args: {
|
|
1140
|
+
tag: string;
|
|
1141
|
+
action: "add" | "remove";
|
|
1142
|
+
changedBy: {
|
|
1143
|
+
type: string;
|
|
1144
|
+
id: string;
|
|
1145
|
+
name?: string;
|
|
1146
|
+
};
|
|
1147
|
+
}): Promise<{
|
|
1148
|
+
ok: boolean;
|
|
1149
|
+
}>;
|
|
1150
|
+
getIdentityChangelog(identityId: string, args?: {
|
|
1151
|
+
limit?: number;
|
|
1152
|
+
}): Promise<{
|
|
1153
|
+
entries: unknown[];
|
|
1154
|
+
}>;
|
|
1155
|
+
rollbackIdentity(identityId: string, args: {
|
|
1156
|
+
targetVersion: number;
|
|
1157
|
+
changedBy: {
|
|
1158
|
+
type: string;
|
|
1159
|
+
id: string;
|
|
1160
|
+
name?: string;
|
|
1161
|
+
};
|
|
1162
|
+
}): Promise<{
|
|
1163
|
+
ok: boolean;
|
|
1164
|
+
entry?: unknown;
|
|
1165
|
+
}>;
|
|
1166
|
+
requestIdentityVerification(args: {
|
|
1167
|
+
claimedIdentityId: string;
|
|
1168
|
+
requestingIdentityId: string;
|
|
1169
|
+
requestingProvider: string;
|
|
1170
|
+
requestingPlatformId: string;
|
|
1171
|
+
preferredChannel?: "mobile" | "email";
|
|
1172
|
+
/**
|
|
1173
|
+
* Phase 2: agent-supplied contact endpoint. When provided, the top-level
|
|
1174
|
+
* `preferredChannel` is ignored — the contact's channel wins.
|
|
1175
|
+
*/
|
|
1176
|
+
contact?: {
|
|
1177
|
+
channel: "email" | "mobile";
|
|
1178
|
+
value: string;
|
|
1179
|
+
};
|
|
1180
|
+
}): Promise<{
|
|
1181
|
+
verificationId: string;
|
|
1182
|
+
channel: string;
|
|
1183
|
+
deliveredTo: string;
|
|
1184
|
+
expiresAt: string;
|
|
1185
|
+
availableChannels: {
|
|
1186
|
+
channel: string;
|
|
1187
|
+
deliveredTo: string;
|
|
1188
|
+
}[];
|
|
1189
|
+
} | {
|
|
1190
|
+
error: string;
|
|
1191
|
+
}>;
|
|
1192
|
+
confirmIdentityVerification(args: {
|
|
1193
|
+
claimedIdentityId: string;
|
|
1194
|
+
verificationId: string;
|
|
1195
|
+
phrase: string;
|
|
1196
|
+
}): Promise<{
|
|
1197
|
+
verified: boolean;
|
|
1198
|
+
identityId?: string;
|
|
1199
|
+
/** Phase 2: how the confirm resolved — Scenario A vs B. */
|
|
1200
|
+
action?: "merged" | "contact_verified";
|
|
1201
|
+
error?: string;
|
|
1202
|
+
}>;
|
|
1203
|
+
/**
|
|
1204
|
+
* Update display-shape fields on an Identity. Body excludes `email` /
|
|
1205
|
+
* `phone` / `title` / `company` / `metadata` per Section D4 — contacts go
|
|
1206
|
+
* via the verify flow, title/company live on OrgMembership, metadata is
|
|
1207
|
+
* not agent-writable.
|
|
1208
|
+
*/
|
|
1209
|
+
updateIdentity(identityId: string, args: {
|
|
1210
|
+
name?: string;
|
|
1211
|
+
avatarUrl?: string;
|
|
1212
|
+
timezone?: string;
|
|
1213
|
+
locale?: string;
|
|
1214
|
+
}): Promise<{
|
|
1215
|
+
ok: boolean;
|
|
1216
|
+
}>;
|
|
1217
|
+
/**
|
|
1218
|
+
* Phase 2 (Section H): server-side verification of a Google Chat sender via
|
|
1219
|
+
* the agent's existing Google OAuth credentials. Returns the resolved
|
|
1220
|
+
* identity (created or matched via Scenario-B email enrichment).
|
|
1221
|
+
*/
|
|
1222
|
+
resolveGoogleChatSender(args: {
|
|
1223
|
+
senderUserId: string;
|
|
1224
|
+
spaceId?: string;
|
|
1225
|
+
}): Promise<{
|
|
1226
|
+
identityId: string | null;
|
|
1227
|
+
status: string;
|
|
1228
|
+
}>;
|
|
1229
|
+
memorySearch(query: string, opts?: {
|
|
1230
|
+
limit?: number;
|
|
1231
|
+
topic?: string;
|
|
1232
|
+
subtopic?: string;
|
|
1233
|
+
tag?: string;
|
|
1234
|
+
includeKnowledge?: boolean;
|
|
1235
|
+
}): Promise<{
|
|
1236
|
+
facts: {
|
|
1237
|
+
subject: string;
|
|
1238
|
+
predicate: string;
|
|
1239
|
+
object: string;
|
|
1240
|
+
since: string;
|
|
1241
|
+
confidence: number;
|
|
1242
|
+
}[];
|
|
1243
|
+
memories: {
|
|
1244
|
+
id: string;
|
|
1245
|
+
text: string;
|
|
1246
|
+
topic: string;
|
|
1247
|
+
subtopic: string;
|
|
1248
|
+
tag: string;
|
|
1249
|
+
importance: number;
|
|
1250
|
+
timestamp: number;
|
|
1251
|
+
score: number;
|
|
1252
|
+
}[];
|
|
1253
|
+
}>;
|
|
1254
|
+
memoryStore(text: string, opts?: {
|
|
1255
|
+
topic?: string;
|
|
1256
|
+
subtopic?: string;
|
|
1257
|
+
tag?: string;
|
|
1258
|
+
importance?: number;
|
|
1259
|
+
}): Promise<{
|
|
1260
|
+
memoryId: string;
|
|
1261
|
+
}>;
|
|
1262
|
+
memoryIngest(sessionKey: string, messages: {
|
|
1263
|
+
role: string;
|
|
1264
|
+
content: string;
|
|
1265
|
+
index: number;
|
|
1266
|
+
timestamp?: string;
|
|
1267
|
+
}[], metadata?: {
|
|
1268
|
+
channelId?: string;
|
|
1269
|
+
userId?: string;
|
|
1270
|
+
userName?: string;
|
|
1271
|
+
}): Promise<{
|
|
1272
|
+
queued: boolean;
|
|
1273
|
+
messageCount: number;
|
|
1274
|
+
}>;
|
|
1275
|
+
memoryLoadContext(tier?: number, topicHint?: string): Promise<{
|
|
1276
|
+
formatted: string;
|
|
1277
|
+
[key: string]: unknown;
|
|
1278
|
+
}>;
|
|
1279
|
+
memoryLookupEntity(subject: string): Promise<{
|
|
1280
|
+
subject: string;
|
|
1281
|
+
triples: {
|
|
1282
|
+
tripleId: string;
|
|
1283
|
+
predicate: string;
|
|
1284
|
+
object: string;
|
|
1285
|
+
validFrom: string;
|
|
1286
|
+
validTo?: string;
|
|
1287
|
+
confidence: number;
|
|
1288
|
+
}[];
|
|
1289
|
+
}>;
|
|
1290
|
+
memoryNavigate(): Promise<{
|
|
1291
|
+
topics: {
|
|
1292
|
+
name: string;
|
|
1293
|
+
tripleCount: number;
|
|
1294
|
+
subtopics: string[];
|
|
1295
|
+
}[];
|
|
1296
|
+
cursor: string | null;
|
|
1297
|
+
}>;
|
|
1298
|
+
memoryDelete(memoryId: string): Promise<{
|
|
1299
|
+
deleted: boolean;
|
|
1300
|
+
}>;
|
|
1301
|
+
memoryStats(): Promise<{
|
|
1302
|
+
vectorCount: number;
|
|
1303
|
+
tripleCount: number;
|
|
1304
|
+
storageEstimateBytes: number;
|
|
1305
|
+
lastIngestionAt?: string;
|
|
1306
|
+
}>;
|
|
1307
|
+
memoryLearn(args: {
|
|
1308
|
+
text: string;
|
|
1309
|
+
source?: string;
|
|
1310
|
+
sourceType?: "file" | "url" | "inline";
|
|
1311
|
+
metadata?: {
|
|
1312
|
+
sessionId?: string;
|
|
1313
|
+
channelId?: string;
|
|
1314
|
+
userName?: string;
|
|
1315
|
+
};
|
|
1316
|
+
}): Promise<{
|
|
1317
|
+
memoriesStored: number;
|
|
1318
|
+
triplesStored: number;
|
|
1319
|
+
chunks: number;
|
|
1320
|
+
source?: string;
|
|
1321
|
+
}>;
|
|
1322
|
+
memoryBootstrapStatus(): Promise<{
|
|
1323
|
+
synced: boolean;
|
|
1324
|
+
syncedAt?: string;
|
|
1325
|
+
sessionsBackfillSynced?: boolean;
|
|
1326
|
+
sessionsBackfillSyncedAt?: string;
|
|
1327
|
+
}>;
|
|
1328
|
+
memoryBootstrapStatusMark(scope?: "files" | "sessions"): Promise<{
|
|
1329
|
+
synced: true;
|
|
1330
|
+
syncedAt: string;
|
|
1331
|
+
}>;
|
|
1332
|
+
sendSms(args: {
|
|
1333
|
+
to: string;
|
|
1334
|
+
body: string;
|
|
1335
|
+
}): Promise<{
|
|
1336
|
+
sent: boolean;
|
|
1337
|
+
sid: string;
|
|
1338
|
+
}>;
|
|
1339
|
+
searchWeb(params: {
|
|
1340
|
+
query: string;
|
|
1341
|
+
count?: number;
|
|
1342
|
+
offset?: number;
|
|
1343
|
+
country?: string;
|
|
1344
|
+
freshness?: string;
|
|
1345
|
+
}): Promise<unknown>;
|
|
1346
|
+
searchImages(params: {
|
|
1347
|
+
query: string;
|
|
1348
|
+
count?: number;
|
|
1349
|
+
}): Promise<unknown>;
|
|
1350
|
+
searchNews(params: {
|
|
1351
|
+
query: string;
|
|
1352
|
+
count?: number;
|
|
1353
|
+
freshness?: string;
|
|
1354
|
+
}): Promise<unknown>;
|
|
1355
|
+
/** Search news across the selected provider's corpus. → POST /agent/news/search */
|
|
1356
|
+
newsSearch(params: {
|
|
1357
|
+
query: string;
|
|
1358
|
+
provider?: NewsProvider;
|
|
1359
|
+
source?: string;
|
|
1360
|
+
from?: string;
|
|
1361
|
+
to?: string;
|
|
1362
|
+
language?: string;
|
|
1363
|
+
category?: string;
|
|
1364
|
+
limit?: number;
|
|
1365
|
+
}): Promise<NewsResult>;
|
|
1366
|
+
/** Top headlines for the selected provider. → POST /agent/news/headlines */
|
|
1367
|
+
newsHeadlines(params?: {
|
|
1368
|
+
provider?: NewsProvider;
|
|
1369
|
+
category?: string;
|
|
1370
|
+
source?: string;
|
|
1371
|
+
language?: string;
|
|
1372
|
+
limit?: number;
|
|
1373
|
+
}): Promise<NewsResult>;
|
|
1374
|
+
/**
|
|
1375
|
+
* Semantic search across the agent's member scopes. Fan-out is gated
|
|
1376
|
+
* server-side by `listScopes` set-inclusion (fail-closed). Pass
|
|
1377
|
+
* `scopeType` + `scopeId` to narrow to one scope; a non-member scope
|
|
1378
|
+
* yields empty results (never a cross-scope leak).
|
|
1379
|
+
*/
|
|
1380
|
+
knowledgeSearch(query: string, opts?: {
|
|
1381
|
+
limit?: number;
|
|
1382
|
+
scopeType?: KnowledgeScopeType;
|
|
1383
|
+
scopeId?: string;
|
|
1384
|
+
}): Promise<KnowledgeSearchResult>;
|
|
1385
|
+
/** Enumerate the scopes (org + teams + projects) this agent belongs to. */
|
|
1386
|
+
listScopes(): Promise<{
|
|
1387
|
+
scopes: KnowledgeScope[];
|
|
1388
|
+
}>;
|
|
1389
|
+
/** Read a scope's structured knowledge profile (after membership check). */
|
|
1390
|
+
getScopeProfile(scopeType: KnowledgeScopeType, scopeId: string): Promise<KnowledgeProfile>;
|
|
1391
|
+
/** List a scope's docs (the org-files corpus; mirrored to shared/<scope>/). */
|
|
1392
|
+
listScopeDocs(scopeType: KnowledgeScopeType, scopeId: string, opts?: {
|
|
1393
|
+
limit?: number;
|
|
1394
|
+
cursor?: string;
|
|
1395
|
+
}): Promise<{
|
|
1396
|
+
files: KnowledgeDoc[];
|
|
1397
|
+
nextCursor: string | null;
|
|
1398
|
+
}>;
|
|
1399
|
+
/**
|
|
1400
|
+
* Read the full text of a scope doc. Resolves a presigned download URL
|
|
1401
|
+
* from `services/org`, then fetches the bytes directly from S3 (the one
|
|
1402
|
+
* legitimate raw fetch in a plugin — same pattern as sync).
|
|
1403
|
+
*/
|
|
1404
|
+
readScopeDoc(scopeType: KnowledgeScopeType, scopeId: string, filePath: string): Promise<{
|
|
1405
|
+
filePath: string;
|
|
1406
|
+
text: string;
|
|
1407
|
+
}>;
|
|
1408
|
+
/**
|
|
1409
|
+
* Write (create or overwrite) a scope doc. Two-step presigned upload:
|
|
1410
|
+
* `services/org` returns a signed URL plus `requiredHeaders` (author /
|
|
1411
|
+
* authorKind / message as `x-amz-meta-*`) that MUST be sent verbatim on
|
|
1412
|
+
* the PUT, alongside the same `Content-Type` that was signed. Author and
|
|
1413
|
+
* authorKind are server-set from the agent token — never trusted here.
|
|
1414
|
+
*/
|
|
1415
|
+
writeScopeDoc(scopeType: KnowledgeScopeType, scopeId: string, filePath: string, content: string, opts?: {
|
|
1416
|
+
contentType?: string;
|
|
1417
|
+
message?: string;
|
|
1418
|
+
}): Promise<{
|
|
1419
|
+
filePath: string;
|
|
1420
|
+
}>;
|
|
1421
|
+
/**
|
|
1422
|
+
* Open a change request against a scope's knowledge resource. For a doc
|
|
1423
|
+
* create/update, `services/org` returns a presigned staging PUT; this method
|
|
1424
|
+
* uploads the proposed `content` to it (echoing the same Content-Type that
|
|
1425
|
+
* was signed), mirroring `writeScopeDoc`. The staged body is applied to the
|
|
1426
|
+
* canonical doc — attributed to this agent — only when a reviewer approves.
|
|
1427
|
+
*/
|
|
1428
|
+
proposeScopeChange(scopeType: KnowledgeScopeType, scopeId: string, input: ProposeScopeChangeInput): Promise<KnowledgeChangeRequest>;
|
|
1429
|
+
/**
|
|
1430
|
+
* List the agent's OWN change requests in a scope (filtered server-side to
|
|
1431
|
+
* this agent as proposer). Pass `status` to narrow to open / approved / etc.
|
|
1432
|
+
*/
|
|
1433
|
+
listScopeChangeRequests(scopeType: KnowledgeScopeType, scopeId: string, opts?: {
|
|
1434
|
+
status?: ChangeRequestStatus;
|
|
1435
|
+
limit?: number;
|
|
1436
|
+
cursor?: string;
|
|
1437
|
+
}): Promise<{
|
|
1438
|
+
changeRequests: KnowledgeChangeRequest[];
|
|
1439
|
+
nextCursor: string | null;
|
|
1440
|
+
}>;
|
|
1441
|
+
registerDatabaseCredentials(): Promise<{
|
|
1442
|
+
connectionString: string;
|
|
1443
|
+
username: string;
|
|
1444
|
+
password: string;
|
|
1445
|
+
databases: string[];
|
|
1446
|
+
}>;
|
|
1447
|
+
reportDatabaseAudit(entry: {
|
|
1448
|
+
database: string;
|
|
1449
|
+
collection: string;
|
|
1450
|
+
operation: string;
|
|
1451
|
+
summary?: string;
|
|
1452
|
+
}): Promise<void>;
|
|
1453
|
+
requestBrowserTakeover(args: {
|
|
1454
|
+
instructions: string;
|
|
1455
|
+
url?: string;
|
|
1456
|
+
conversationId?: string;
|
|
1457
|
+
}): Promise<{
|
|
1458
|
+
sessionId: string;
|
|
1459
|
+
status: string;
|
|
1460
|
+
}>;
|
|
1461
|
+
getRemoteSession(sessionId: string): Promise<RemoteSessionInfo>;
|
|
1462
|
+
completeRemoteSession(sessionId: string): Promise<{
|
|
1463
|
+
ok: boolean;
|
|
1464
|
+
}>;
|
|
1465
|
+
/**
|
|
1466
|
+
* Issue a request that returns the raw `Response` (no JSON parsing, no
|
|
1467
|
+
* forced Content-Type). The caller sets `Content-Type`/`Accept` on `headers`
|
|
1468
|
+
* and reads the body itself (`arrayBuffer()` / `json()`).
|
|
1469
|
+
*
|
|
1470
|
+
* A single retry fires only on the same transient statuses `request()`
|
|
1471
|
+
* retries (authorizer-timeout 500 + LB 502/503/504) and transient network
|
|
1472
|
+
* errors — before the route handler runs — so re-issuing a POST does not
|
|
1473
|
+
* risk a duplicate side effect. Voice TTS/STT are effectively idempotent
|
|
1474
|
+
* (re-synthesize / re-transcribe) and meter server-side keyed on the
|
|
1475
|
+
* gateway requestId, so a retried transcription doesn't double-bill.
|
|
1476
|
+
*/
|
|
1477
|
+
private rawFetch;
|
|
1478
|
+
/**
|
|
1479
|
+
* Text-to-speech. Returns raw PCM audio bytes plus their framing — the
|
|
1480
|
+
* voice service defaults to 24 kHz / mono / 16-bit. Wrap in a WAV container
|
|
1481
|
+
* to produce a playable file. Metered per character against the tenant
|
|
1482
|
+
* credit pool server-side; TTS completes regardless of metering outcome.
|
|
1483
|
+
*/
|
|
1484
|
+
tts(args: VoiceTtsArgs): Promise<VoiceTtsResult>;
|
|
1485
|
+
/**
|
|
1486
|
+
* Speech-to-text. Accepts raw linear16 (16-bit LE) mono PCM — NOT a WAV or
|
|
1487
|
+
* other container (the endpoint transcribes with a fixed linear16 encoding,
|
|
1488
|
+
* so a container header would be transcribed as noise). Strip any WAV header
|
|
1489
|
+
* and pass `sampleRate` from it before calling. Metered by transcribed
|
|
1490
|
+
* duration against the tenant credit pool server-side.
|
|
1491
|
+
*/
|
|
1492
|
+
stt(args: VoiceSttArgs): Promise<VoiceSttResult>;
|
|
1493
|
+
}
|
|
1494
|
+
//# sourceMappingURL=index.d.ts.map
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#endregion
|
|
1497
|
+
//#region ../chat/dist/index.d.ts
|
|
1498
|
+
//#region src/types.d.ts
|
|
1499
|
+
/**
|
|
1500
|
+
* Types for the @alfe.ai/chat package.
|
|
1501
|
+
*/
|
|
1502
|
+
/** Configuration for the ChatServiceClient. */
|
|
1503
|
+
interface ChatServiceClientOptions {
|
|
1504
|
+
/** Chat service WebSocket URL (e.g. wss://chat.dev.alfe.ai/ws) */
|
|
1505
|
+
wsUrl: string;
|
|
1506
|
+
/** Agent API key for Bearer auth */
|
|
1507
|
+
apiKey: string;
|
|
1508
|
+
/** Called when the chat service sends an RPC request */
|
|
1509
|
+
onRequest: (request: RpcRequest) => void;
|
|
1510
|
+
/** Called when connection state changes */
|
|
1511
|
+
onConnectionChange?: (connected: boolean) => void;
|
|
1512
|
+
/** Optional logger (defaults to console) */
|
|
1513
|
+
logger?: Logger;
|
|
1514
|
+
}
|
|
1515
|
+
/** An RPC request from the chat service. */
|
|
1516
|
+
interface RpcRequest {
|
|
1517
|
+
type: 'req';
|
|
1518
|
+
id: string;
|
|
1519
|
+
method: string;
|
|
1520
|
+
params: Record<string, unknown>;
|
|
1521
|
+
}
|
|
1522
|
+
/** Minimal logger interface — no dependency on any logging library. */
|
|
1523
|
+
interface Logger {
|
|
1524
|
+
info(msg: string, ...args: unknown[]): void;
|
|
1525
|
+
warn(msg: string, ...args: unknown[]): void;
|
|
1526
|
+
error(msg: string, ...args: unknown[]): void;
|
|
1527
|
+
debug(msg: string, ...args: unknown[]): void;
|
|
1528
|
+
}
|
|
1529
|
+
//#endregion
|
|
1530
|
+
//#region src/client.d.ts
|
|
1531
|
+
|
|
1532
|
+
declare class ChatServiceClient {
|
|
1533
|
+
private readonly options;
|
|
1534
|
+
private ws;
|
|
1535
|
+
private stopped;
|
|
1536
|
+
private connected;
|
|
1537
|
+
private retryCount;
|
|
1538
|
+
private retryTimer;
|
|
1539
|
+
private heartbeatTimer;
|
|
1540
|
+
private isAlive;
|
|
1541
|
+
private readonly log;
|
|
1542
|
+
private pendingRequests;
|
|
1543
|
+
private requestCounter;
|
|
1544
|
+
private messageQueue;
|
|
1545
|
+
private readonly MAX_QUEUE_SIZE;
|
|
1546
|
+
constructor(options: ChatServiceClientOptions);
|
|
1547
|
+
get isConnected(): boolean;
|
|
1548
|
+
/** Start connecting to the chat service. */
|
|
1549
|
+
start(): void;
|
|
1550
|
+
/** Stop the client and close the connection. */
|
|
1551
|
+
stop(): void;
|
|
1552
|
+
/** Send a raw JSON message to the chat service. Queues if disconnected. */
|
|
1553
|
+
send(msg: unknown): void;
|
|
1554
|
+
/** Send an RPC response back to the chat service. */
|
|
1555
|
+
sendResponse(id: string, ok: boolean, payload?: unknown): void;
|
|
1556
|
+
/** Send a streaming event to the chat service (e.g. chat deltas). */
|
|
1557
|
+
sendEvent(event: string, payload: unknown): void;
|
|
1558
|
+
/** Send a fire-and-forget notification to the chat service. */
|
|
1559
|
+
notify(event: string, payload: Record<string, unknown>): void;
|
|
1560
|
+
/**
|
|
1561
|
+
* Send an RPC request to the chat service and wait for a response.
|
|
1562
|
+
* Used by agent-to-agent tools to invoke server-side logic.
|
|
1563
|
+
*/
|
|
1564
|
+
sendRequest(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<Record<string, unknown>>;
|
|
1565
|
+
private flushMessageQueue;
|
|
1566
|
+
private flushPendingRequests;
|
|
1567
|
+
private doConnect;
|
|
1568
|
+
private handleMessage;
|
|
1569
|
+
private scheduleReconnect;
|
|
1570
|
+
/**
|
|
1571
|
+
* Detect a silently-dropped connection. Each tick: if no inbound liveness
|
|
1572
|
+
* (pong/ping/message) was seen since the last tick, force-close so the
|
|
1573
|
+
* `close` handler reconnects; otherwise send a fresh ping and arm the next.
|
|
1574
|
+
*/
|
|
1575
|
+
private startHeartbeat;
|
|
1576
|
+
private clearHeartbeat;
|
|
1577
|
+
}
|
|
1578
|
+
//#endregion
|
|
1579
|
+
//#region src/config.d.ts
|
|
1580
|
+
/**
|
|
1581
|
+
* Chat config resolver — reads API key from ~/.alfe/config.toml
|
|
1582
|
+
* and derives the chat service URL from the token prefix.
|
|
1583
|
+
*
|
|
1584
|
+
* Resolution priority: explicit overrides → config file → env vars.
|
|
1585
|
+
*/
|
|
1586
|
+
//#endregion
|
|
1587
|
+
//#region src/stream-mapper.d.ts
|
|
1588
|
+
/**
|
|
1589
|
+
* Claude Code `--output-format stream-json` → Alfe chat-activity mapper.
|
|
1590
|
+
*
|
|
1591
|
+
* This is the load-bearing core of the bridge. It consumes the newline-delimited
|
|
1592
|
+
* JSON events emitted by `claude -p ... --output-format stream-json
|
|
1593
|
+
* --include-partial-messages` and translates them into the SAME wire shapes the
|
|
1594
|
+
* `@alfe.ai/openclaw-chat` plugin emits, so the chat service / adapter / UI
|
|
1595
|
+
* treat a Claude Code turn identically to an OpenClaw turn.
|
|
1596
|
+
*
|
|
1597
|
+
* Ground truth: a real captured turn (thinking + Read tool + text + result) at
|
|
1598
|
+
* `src/__fixtures__/turn-read-tool.stream.jsonl`. The mapper test runs against
|
|
1599
|
+
* that fixture (TEST-FIRST).
|
|
1600
|
+
*
|
|
1601
|
+
* Event model (verified against the fixture + Claude Code stream-json docs):
|
|
1602
|
+
* - `{type:'system', subtype:'init', session_id}` — carries the claude
|
|
1603
|
+
* session id we persist for `--resume`.
|
|
1604
|
+
* - `{type:'stream_event', event:{type:'content_block_delta', delta:{
|
|
1605
|
+
* type:'thinking_delta', thinking}}}` — a thinking chunk.
|
|
1606
|
+
* - `{type:'stream_event', event:{type:'content_block_delta', delta:{
|
|
1607
|
+
* type:'text_delta', text}}}` — a visible-text chunk (accumulate → the
|
|
1608
|
+
* CUMULATIVE snapshot the adapter length-slices).
|
|
1609
|
+
* - `{type:'assistant', message:{content:[{type:'tool_use', id, name,
|
|
1610
|
+
* input}]}}` — a tool CALL. The non-streaming `assistant` message has the
|
|
1611
|
+
* COMPLETE id/name/input (the streaming `content_block_start` has empty
|
|
1612
|
+
* input, filled by `input_json_delta`), so we correlate off THIS.
|
|
1613
|
+
* - `{type:'user', message:{content:[{type:'tool_result', tool_use_id,
|
|
1614
|
+
* content, is_error?}]}}` — a tool RESULT. `tool_use_id === tool_use.id`.
|
|
1615
|
+
* - `{type:'result', is_error, result, duration_ms}` — the turn end.
|
|
1616
|
+
*
|
|
1617
|
+
* The mapper is a PURE reducer over one event: it takes the running state and an
|
|
1618
|
+
* event, mutates the state, and returns the list of `MapperEmission`s to send.
|
|
1619
|
+
* It performs NO IO — the host does the sending / persisting. `progressText` /
|
|
1620
|
+
* the 250ms mid-tool throttle from openclaw-chat is inert here: Claude Code has
|
|
1621
|
+
* no mid-tool progress analog, so we deliberately don't implement it.
|
|
1622
|
+
*/
|
|
1623
|
+
interface ThinkingActivity {
|
|
1624
|
+
kind: 'thinking';
|
|
1625
|
+
text: string;
|
|
1626
|
+
delta?: boolean;
|
|
1627
|
+
ts?: number;
|
|
1628
|
+
seq?: number;
|
|
1629
|
+
}
|
|
1630
|
+
interface ToolActivity {
|
|
1631
|
+
kind: 'tool';
|
|
1632
|
+
toolCallId: string;
|
|
1633
|
+
name: string;
|
|
1634
|
+
status: 'running' | 'done' | 'failed';
|
|
1635
|
+
summary?: string;
|
|
1636
|
+
argsText?: string;
|
|
1637
|
+
resultText?: string;
|
|
1638
|
+
isError?: boolean;
|
|
1639
|
+
durationMs?: number;
|
|
1640
|
+
truncated?: {
|
|
1641
|
+
args?: boolean;
|
|
1642
|
+
result?: boolean;
|
|
1643
|
+
};
|
|
1644
|
+
ts?: number;
|
|
1645
|
+
seq?: number;
|
|
1646
|
+
}
|
|
1647
|
+
type ActivityEmission = {
|
|
1648
|
+
type: 'chat-activity';
|
|
1649
|
+
seq: number;
|
|
1650
|
+
ts: number;
|
|
1651
|
+
activity: ThinkingActivity;
|
|
1652
|
+
} | {
|
|
1653
|
+
type: 'chat-activity';
|
|
1654
|
+
seq: number;
|
|
1655
|
+
ts: number;
|
|
1656
|
+
activity: ToolActivity;
|
|
1657
|
+
};
|
|
1658
|
+
/** A cumulative visible-text snapshot → a `chat` delta frame (B2). */
|
|
1659
|
+
interface TextEmission {
|
|
1660
|
+
type: 'chat-delta';
|
|
1661
|
+
seq: number;
|
|
1662
|
+
ts: number;
|
|
1663
|
+
/** CUMULATIVE visible-so-far. Monotonically non-shrinking across the turn. */
|
|
1664
|
+
text: string;
|
|
1665
|
+
}
|
|
1666
|
+
/** The turn ended. `isError` routes the host to the C3 failure path. */
|
|
1667
|
+
interface EndEmission {
|
|
1668
|
+
type: 'turn-end';
|
|
1669
|
+
seq: number;
|
|
1670
|
+
ts: number;
|
|
1671
|
+
isError: boolean;
|
|
1672
|
+
/** Final result text (present on success `result`); '' on error. */
|
|
1673
|
+
finalText: string;
|
|
1674
|
+
durationMs?: number;
|
|
1675
|
+
/** Error reason (present when `isError`). Goes under `payload.message` (C3). */
|
|
1676
|
+
errorMessage?: string;
|
|
1677
|
+
}
|
|
1678
|
+
type MapperEmission = ActivityEmission | TextEmission | EndEmission;
|
|
1679
|
+
interface PendingTool {
|
|
1680
|
+
name: string;
|
|
1681
|
+
argsText?: string;
|
|
1682
|
+
argsTruncated?: boolean;
|
|
1683
|
+
startTs: number;
|
|
1684
|
+
}
|
|
1685
|
+
interface MapperState {
|
|
1686
|
+
/** Claude session id from the `init` event (persist for `--resume`). */
|
|
1687
|
+
claudeSessionId: string | null;
|
|
1688
|
+
/** Monotonic per-turn seq stamped on every emission (E1). */
|
|
1689
|
+
seq: number;
|
|
1690
|
+
/** CUMULATIVE visible text (B2 — never shrinks). */
|
|
1691
|
+
visibleText: string;
|
|
1692
|
+
/** tool_use.id → correlation record, joined on the later tool_result. */
|
|
1693
|
+
pendingTools: Map<string, PendingTool>;
|
|
1694
|
+
/** Whether the terminal `result` event has been seen. */
|
|
1695
|
+
ended: boolean;
|
|
1696
|
+
}
|
|
1697
|
+
declare function createMapperState(): MapperState;
|
|
1698
|
+
/**
|
|
1699
|
+
* Map ONE stream-json event → zero or more emissions, mutating `state`.
|
|
1700
|
+
*
|
|
1701
|
+
* `nowMs` lets the host stamp real wall-clock timestamps (Claude Code events
|
|
1702
|
+
* don't all carry ms epochs); the fixture test injects a fixed clock.
|
|
1703
|
+
*/
|
|
1704
|
+
declare function mapStreamEvent(state: MapperState, raw: unknown, nowMs: number): MapperEmission[];
|
|
1705
|
+
/**
|
|
1706
|
+
* Convenience: run the reducer over a whole array of parsed events (e.g. a
|
|
1707
|
+
* fixture). Returns the final state + all emissions in order. The host does NOT
|
|
1708
|
+
* use this at runtime (it maps line-by-line as they stream), but it's the
|
|
1709
|
+
* cleanest thing to assert against in a fixture test.
|
|
1710
|
+
*/
|
|
1711
|
+
declare function mapStreamEvents(events: unknown[], clock?: () => number): {
|
|
1712
|
+
state: MapperState;
|
|
1713
|
+
emissions: MapperEmission[];
|
|
1714
|
+
};
|
|
1715
|
+
/**
|
|
1716
|
+
* Terminalize the mapper's in-turn activity into persisted `ChatActivityRecord`
|
|
1717
|
+
* form (session store). Tools still pending at flush time (no tool_result seen)
|
|
1718
|
+
* are persisted as `interrupted`. Thinking deltas are concatenated into ONE
|
|
1719
|
+
* record. This mirrors openclaw-chat's per-turn accumulator flush.
|
|
1720
|
+
*/
|
|
1721
|
+
declare function collectTurnActivityRecords(emissions: MapperEmission[]): {
|
|
1722
|
+
kind: 'thinking' | 'tool';
|
|
1723
|
+
[k: string]: unknown;
|
|
1724
|
+
}[];
|
|
1725
|
+
//#endregion
|
|
1726
|
+
//#region src/session-driver.d.ts
|
|
1727
|
+
/**
|
|
1728
|
+
* `--input-format stream-json` does not keep a headless `claude -p` process
|
|
1729
|
+
* resident across turns on the pinned CLI (see header VERIFICATION). Until a
|
|
1730
|
+
* CLI ships a resident headless stdin channel, warm-streaming falls back to
|
|
1731
|
+
* per-turn spawn. Flip this (and implement a resident path) when that lands.
|
|
1732
|
+
*/
|
|
1733
|
+
declare const WARM_STREAMING_SUPPORTED = false;
|
|
1734
|
+
/**
|
|
1735
|
+
* Restricted tool set for unattended bridged turns (Phase 2 tool-permission
|
|
1736
|
+
* posture). An inbound chat message could try to induce a destructive shell
|
|
1737
|
+
* command, and these turns run unattended on the user's machine. We therefore
|
|
1738
|
+
* pass an explicit allow-list of read/analysis/edit tools and DENY Bash by
|
|
1739
|
+
* default, and use `--permission-mode default` (NEVER `bypassPermissions` /
|
|
1740
|
+
* `--dangerously-skip-permissions`). See DEVELOPING.md → "Tool-permission
|
|
1741
|
+
* posture". Bash is intentionally absent — a bridged turn cannot run arbitrary
|
|
1742
|
+
* shell. Callers that want a wider set pass `allowedTools` explicitly.
|
|
1743
|
+
*/
|
|
1744
|
+
declare const DEFAULT_ALLOWED_TOOLS: readonly ["Read", "Grep", "Glob", "LS", "Edit", "Write", "WebFetch", "WebSearch", "Task", "TodoWrite"];
|
|
1745
|
+
/** Sane default permission posture. NEVER `bypassPermissions`. */
|
|
1746
|
+
declare const DEFAULT_PERMISSION_MODE = "default";
|
|
1747
|
+
/** How a turn ended, surfaced to the host so it can settle / persist. */
|
|
1748
|
+
interface DriverTurnResult {
|
|
1749
|
+
/** The claude session id observed on the `system:init` event (for --resume). */
|
|
1750
|
+
claudeSessionId: string | null;
|
|
1751
|
+
/** All emissions produced during the turn, in order (for activity persistence). */
|
|
1752
|
+
emissions: MapperEmission[];
|
|
1753
|
+
/** True if the terminal `result` event carried `is_error`, or the child failed. */
|
|
1754
|
+
isError: boolean;
|
|
1755
|
+
/** Final visible/result text (cumulative). '' on error. */
|
|
1756
|
+
finalText: string;
|
|
1757
|
+
/** Error reason when `isError` (goes under `payload.message` — C3). */
|
|
1758
|
+
errorMessage?: string;
|
|
1759
|
+
/** True if the turn was aborted via `abort()` (SIGINT). */
|
|
1760
|
+
aborted: boolean;
|
|
1761
|
+
}
|
|
1762
|
+
interface RunTurnParams {
|
|
1763
|
+
/** The user prompt for this turn. */
|
|
1764
|
+
prompt: string;
|
|
1765
|
+
/** Claude session id to `--resume`; null → start a fresh session. */
|
|
1766
|
+
claudeSessionId: string | null;
|
|
1767
|
+
/** Working directory (the agent workspace). */
|
|
1768
|
+
cwd: string;
|
|
1769
|
+
/** Path to the generated MCP config JSON. Omitted → no `--mcp-config`. */
|
|
1770
|
+
mcpConfigPath?: string;
|
|
1771
|
+
/** Persona + reply-style text appended to the system prompt. Omitted → none. */
|
|
1772
|
+
appendSystemPrompt?: string;
|
|
1773
|
+
/** Tool allow-list (defaults to DEFAULT_ALLOWED_TOOLS). */
|
|
1774
|
+
allowedTools?: readonly string[];
|
|
1775
|
+
/** Permission mode (defaults to DEFAULT_PERMISSION_MODE — never bypass). */
|
|
1776
|
+
permissionMode?: string;
|
|
1777
|
+
/** Per-emission callback so the host can stream deltas/activity live. */
|
|
1778
|
+
onEmission: (emission: MapperEmission) => void;
|
|
1779
|
+
/** Wall-clock override for the mapper (tests). Defaults to Date.now. */
|
|
1780
|
+
now?: () => number;
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Hard cap on a single turn (E2). Deliberately BELOW the chat service's RPC
|
|
1784
|
+
* timeout (`AGENT_TIMEOUT_SEC=300` → 305s in daemon-server) so the host settles
|
|
1785
|
+
* a maxed-out turn *before* the caller rejects it — an equal cap races the
|
|
1786
|
+
* caller and can surface a spurious "stuck then error" flash. Inject-origin
|
|
1787
|
+
* turns have an even shorter caller timeout (~125s); those may over-run to this
|
|
1788
|
+
* cap, but the late settle is harmlessly no-op'd by the chat service.
|
|
1789
|
+
*/
|
|
1790
|
+
declare const TURN_TIMEOUT_MS = 300000;
|
|
1791
|
+
/**
|
|
1792
|
+
* Spawns and drives `claude` subprocesses. Tracks a SINGLE in-flight child, so
|
|
1793
|
+
* the host creates ONE driver PER TURN (`ClaudeCodeHost.makeDriver`) — a driver
|
|
1794
|
+
* shared across concurrent sessions would let the last spawn overwrite the
|
|
1795
|
+
* `child` slot and cross-abort / mis-probe the others.
|
|
1796
|
+
*/
|
|
1797
|
+
declare class SessionDriver {
|
|
1798
|
+
private child;
|
|
1799
|
+
private readonly bin;
|
|
1800
|
+
private readonly baseEnv;
|
|
1801
|
+
private readonly log;
|
|
1802
|
+
/** Injected spawn — tests replace this with a fake child. */
|
|
1803
|
+
private readonly spawnImpl;
|
|
1804
|
+
constructor(opts?: {
|
|
1805
|
+
claudeBin?: string;
|
|
1806
|
+
baseEnv?: NodeJS.ProcessEnv;
|
|
1807
|
+
logger?: Logger;
|
|
1808
|
+
/** Test seam: inject a fake `spawn`. */
|
|
1809
|
+
__spawn?: typeof spawn;
|
|
1810
|
+
});
|
|
1811
|
+
/** True while a `claude` child is running — backs the host's B4 drain probe. */
|
|
1812
|
+
get busy(): boolean;
|
|
1813
|
+
/**
|
|
1814
|
+
* Abort the in-flight turn by SIGINT-ing the child (B3). No-op if idle. The
|
|
1815
|
+
* child's exit drives `runTurn` to settle with `aborted: true`.
|
|
1816
|
+
*/
|
|
1817
|
+
abort(): void;
|
|
1818
|
+
/**
|
|
1819
|
+
* Run ONE turn. Modes reconcile to the same per-turn spawn (see header
|
|
1820
|
+
* VERIFICATION) — `voice` is accepted for API symmetry and future warm mode.
|
|
1821
|
+
*/
|
|
1822
|
+
runTurn(params: RunTurnParams, mode?: 'chat' | 'voice'): Promise<DriverTurnResult>;
|
|
1823
|
+
private buildArgs;
|
|
1824
|
+
private runPerTurn;
|
|
1825
|
+
}
|
|
1826
|
+
//#endregion
|
|
1827
|
+
//#region src/host.d.ts
|
|
1828
|
+
interface ClaudeCodeHostConfig {
|
|
1829
|
+
/** Chat service WS URL (e.g. wss://chat.dev.alfe.ai/ws). */
|
|
1830
|
+
wsUrl: string;
|
|
1831
|
+
/** Agent API key (Bearer auth for both chat WS and AgentApiClient). */
|
|
1832
|
+
apiKey: string;
|
|
1833
|
+
/** Base URL for the Alfe agent API (AgentApiClient). */
|
|
1834
|
+
apiUrl: string;
|
|
1835
|
+
/** Agent workspace dir (cwd for `claude`, source of persona files). */
|
|
1836
|
+
workspace: string;
|
|
1837
|
+
/** Path to the generated MCP config JSON (memory/voice/send + integrations). */
|
|
1838
|
+
mcpConfigPath?: string;
|
|
1839
|
+
/** Path to persist Alfe sessionId → claude session id. */
|
|
1840
|
+
claudeSessionsPath?: string;
|
|
1841
|
+
logger?: Logger;
|
|
1842
|
+
}
|
|
1843
|
+
declare class ClaudeCodeHost {
|
|
1844
|
+
private readonly chat;
|
|
1845
|
+
/**
|
|
1846
|
+
* Factory for a per-turn `SessionDriver`. A driver tracks a single in-flight
|
|
1847
|
+
* `child`, so distinct sessions running concurrently (C4) each get their OWN
|
|
1848
|
+
* driver — a shared singleton would cross-abort and mis-probe (its one `child`
|
|
1849
|
+
* slot would be overwritten by whichever session spawned last).
|
|
1850
|
+
*/
|
|
1851
|
+
private readonly makeDriver;
|
|
1852
|
+
private readonly api;
|
|
1853
|
+
private readonly log;
|
|
1854
|
+
private readonly cfg;
|
|
1855
|
+
private readonly queues;
|
|
1856
|
+
/** Alfe sessionId → claude session id map (mirrors ~/.alfe/claude-code/sessions.json). */
|
|
1857
|
+
private readonly claudeSessionIds;
|
|
1858
|
+
private claudeSessionsPath;
|
|
1859
|
+
/** RPC sessionKey → currently RUNNING request id (for chat.abort targeting). */
|
|
1860
|
+
private readonly runningTurns;
|
|
1861
|
+
/** idempotencyKey → settled request id, for E3 duplicate-RPC dedupe. */
|
|
1862
|
+
private readonly seenIdempotencyKeys;
|
|
1863
|
+
/** RPC sessionKey → resolved SessionDriver so chat.abort can SIGINT the child. */
|
|
1864
|
+
private activeDriverBySession;
|
|
1865
|
+
constructor(cfg: ClaudeCodeHostConfig, deps?: {
|
|
1866
|
+
/** Per-turn driver factory (test seam). Called once per turn. */
|
|
1867
|
+
makeDriver?: () => SessionDriver;
|
|
1868
|
+
chat?: ChatServiceClient;
|
|
1869
|
+
api?: AgentApiClient;
|
|
1870
|
+
});
|
|
1871
|
+
/** Start the chat WS. Non-blocking — the client reconnects internally. */
|
|
1872
|
+
start(): Promise<void>;
|
|
1873
|
+
/** Stop the chat WS (SIGTERM path). */
|
|
1874
|
+
stop(): void;
|
|
1875
|
+
/**
|
|
1876
|
+
* Inbound RPC dispatch. Invoked by the chat client's `onRequest` in
|
|
1877
|
+
* production; also the test seam (drive `agent`/`chat.abort`/… directly).
|
|
1878
|
+
*/
|
|
1879
|
+
handleRequest(req: RpcRequest): Promise<void>;
|
|
1880
|
+
private handleAgent;
|
|
1881
|
+
private runAgentTurn;
|
|
1882
|
+
/**
|
|
1883
|
+
* Run a turn; on a `--resume` failure (stale/missing claude session id) start
|
|
1884
|
+
* fresh and prepend the last-N stored messages so the model keeps context.
|
|
1885
|
+
*/
|
|
1886
|
+
private runWithResumeFallback;
|
|
1887
|
+
private forwardEmission;
|
|
1888
|
+
private handleChatAbort;
|
|
1889
|
+
private handleSessionsList;
|
|
1890
|
+
private handleSessionsGet;
|
|
1891
|
+
private buildSystemPrompt;
|
|
1892
|
+
private readPersona;
|
|
1893
|
+
private buildHistoryPreamble;
|
|
1894
|
+
private ensureSession;
|
|
1895
|
+
private loadClaudeSessionIds;
|
|
1896
|
+
private saveClaudeSessionIds;
|
|
1897
|
+
private pruneIdempotency;
|
|
1898
|
+
}
|
|
1899
|
+
/** Strip the `agent:{agentId}:` prefix from a full server session key. */
|
|
1900
|
+
declare function stripAgentPrefix(sessionKey: string): string;
|
|
1901
|
+
//#endregion
|
|
1902
|
+
//#region src/child-env.d.ts
|
|
1903
|
+
/**
|
|
1904
|
+
* Child-process environment construction for spawned `claude` CLI processes.
|
|
1905
|
+
*
|
|
1906
|
+
* TWO non-negotiable invariants (Phase 2 plan, audit B2 + the hard
|
|
1907
|
+
* constraints):
|
|
1908
|
+
*
|
|
1909
|
+
* 1. **Scrub `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` /
|
|
1910
|
+
* `ANTHROPIC_AUTH_TOKEN`.** A self-hosted Mac very often has these set (for
|
|
1911
|
+
* other tooling). If ANY of them reaches `claude`, the CLI silently switches
|
|
1912
|
+
* from the user's Claude subscription (OAuth) to API-key/proxy auth — which
|
|
1913
|
+
* is exactly what this whole runtime exists to avoid (no Agent SDK, no
|
|
1914
|
+
* proxy, subscription only). The break is SILENT: the turn "works" but bills
|
|
1915
|
+
* the wrong plane. So we strip them unconditionally from the child env.
|
|
1916
|
+
*
|
|
1917
|
+
* 2. **Set `ALFE_CLAUDE_BRIDGE=1`.** The Phase-1 CLI hooks
|
|
1918
|
+
* (`alfe claude hook ...` in `packages/cli/src/commands/claude.ts`) check
|
|
1919
|
+
* `process.env.ALFE_CLAUDE_BRIDGE` FIRST and no-op when set, so the host —
|
|
1920
|
+
* not the interactive-session hooks — owns memory recall/capture for bridged
|
|
1921
|
+
* turns. Omitting this double-captures every message. The env var NAME is a
|
|
1922
|
+
* cross-phase contract; do not rename without updating the CLI.
|
|
1923
|
+
*/
|
|
1924
|
+
/** Anthropic env vars that force API-key / proxy auth and MUST be scrubbed. */
|
|
1925
|
+
declare const SCRUBBED_ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"];
|
|
1926
|
+
/** Cross-phase contract marker (see header + packages/cli/src/commands/claude.ts). */
|
|
1927
|
+
declare const BRIDGE_ENV_VAR = "ALFE_CLAUDE_BRIDGE";
|
|
1928
|
+
/**
|
|
1929
|
+
* Build the env handed to a spawned `claude` child. Pure — takes the base env
|
|
1930
|
+
* explicitly so it unit-tests without touching `process.env`.
|
|
1931
|
+
*
|
|
1932
|
+
* - Every scrubbed Anthropic var is DELETED (not blanked) so `claude`'s
|
|
1933
|
+
* env-detection treats it as absent.
|
|
1934
|
+
* - `ALFE_CLAUDE_BRIDGE=1` is set.
|
|
1935
|
+
* - Any caller-supplied `extra` entries are layered last (e.g. a per-turn
|
|
1936
|
+
* marker) but can NEVER re-introduce a scrubbed var — the scrub runs after
|
|
1937
|
+
* the merge as the final gate.
|
|
1938
|
+
*/
|
|
1939
|
+
declare function buildChildEnv(base: NodeJS.ProcessEnv, extra?: Record<string, string>): NodeJS.ProcessEnv;
|
|
1940
|
+
//#endregion
|
|
1941
|
+
//#region src/session/session-store.d.ts
|
|
1942
|
+
type StoredMessageComponent = {
|
|
1943
|
+
type: 'link_button';
|
|
1944
|
+
id: string;
|
|
1945
|
+
label: string;
|
|
1946
|
+
url: string;
|
|
1947
|
+
target?: 'same-tab' | 'new-tab' | 'popup';
|
|
1948
|
+
style?: 'primary' | 'secondary' | 'danger';
|
|
1949
|
+
} | {
|
|
1950
|
+
type: 'quick_reply';
|
|
1951
|
+
id: string;
|
|
1952
|
+
label: string;
|
|
1953
|
+
value: string;
|
|
1954
|
+
style?: 'primary' | 'secondary' | 'danger';
|
|
1955
|
+
};
|
|
1956
|
+
/**
|
|
1957
|
+
* Persisted reference to a message attachment. Deliberately stores NO URL —
|
|
1958
|
+
* the download URL is short-lived and re-signed on demand. Mirrors
|
|
1959
|
+
* openclaw-chat's `StoredAttachment`.
|
|
1960
|
+
*/
|
|
1961
|
+
interface StoredAttachment {
|
|
1962
|
+
attachmentId: string;
|
|
1963
|
+
type: 'image' | 'video' | 'audio' | 'document' | 'file';
|
|
1964
|
+
mimeType: string;
|
|
1965
|
+
filename: string;
|
|
1966
|
+
size?: number;
|
|
1967
|
+
}
|
|
1968
|
+
interface ChatMessage {
|
|
1969
|
+
role: 'user' | 'assistant';
|
|
1970
|
+
content: string;
|
|
1971
|
+
timestamp: number;
|
|
1972
|
+
senderId?: string;
|
|
1973
|
+
senderName?: string;
|
|
1974
|
+
components?: StoredMessageComponent[];
|
|
1975
|
+
attachments?: StoredAttachment[];
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Persisted form of a turn's agent activity (tool cards + thinking), so a
|
|
1979
|
+
* refresh can replay what the live `chat-activity` stream showed. Terminal by
|
|
1980
|
+
* construction: no `progressText`, no `running` status — a tool still running
|
|
1981
|
+
* at flush time persists as `interrupted`.
|
|
1982
|
+
*/
|
|
1983
|
+
type ChatActivityRecord = {
|
|
1984
|
+
kind: 'thinking';
|
|
1985
|
+
text: string;
|
|
1986
|
+
ts: number;
|
|
1987
|
+
} | {
|
|
1988
|
+
kind: 'tool';
|
|
1989
|
+
toolCallId: string;
|
|
1990
|
+
name: string;
|
|
1991
|
+
status: 'done' | 'failed' | 'interrupted';
|
|
1992
|
+
summary?: string;
|
|
1993
|
+
argsText?: string;
|
|
1994
|
+
resultText?: string;
|
|
1995
|
+
isError?: boolean;
|
|
1996
|
+
durationMs?: number;
|
|
1997
|
+
truncated?: {
|
|
1998
|
+
args?: boolean;
|
|
1999
|
+
progress?: boolean;
|
|
2000
|
+
result?: boolean;
|
|
2001
|
+
};
|
|
2002
|
+
ts: number;
|
|
2003
|
+
};
|
|
2004
|
+
interface SessionData {
|
|
2005
|
+
sessionId: string;
|
|
2006
|
+
agentId: string;
|
|
2007
|
+
channel: string;
|
|
2008
|
+
tenantId?: string;
|
|
2009
|
+
userId?: string;
|
|
2010
|
+
createdAt: string;
|
|
2011
|
+
updatedAt: string;
|
|
2012
|
+
messages: ChatMessage[];
|
|
2013
|
+
/**
|
|
2014
|
+
* Activity records replayed on `sessions.get`. Unlike openclaw-chat (whose
|
|
2015
|
+
* PRIMARY source is OpenClaw's transcript), these records are the SOLE
|
|
2016
|
+
* history source here — there is no external transcript. Absent on
|
|
2017
|
+
* pre-persistence files.
|
|
2018
|
+
*/
|
|
2019
|
+
activity?: ChatActivityRecord[];
|
|
2020
|
+
}
|
|
2021
|
+
interface SessionSummary {
|
|
2022
|
+
sessionId: string;
|
|
2023
|
+
agentId: string;
|
|
2024
|
+
channel: string;
|
|
2025
|
+
tenantId?: string;
|
|
2026
|
+
userId?: string;
|
|
2027
|
+
createdAt: string;
|
|
2028
|
+
lastMessageAt?: string;
|
|
2029
|
+
preview?: string;
|
|
2030
|
+
messageCount: number;
|
|
2031
|
+
}
|
|
2032
|
+
declare function getSession(sessionId: string): Promise<SessionData | null>;
|
|
2033
|
+
declare function saveSession(session: SessionData): Promise<void>;
|
|
2034
|
+
declare function createSession(sessionId: string, agentId: string, channel: string, tenantId?: string, userId?: string): Promise<SessionData>;
|
|
2035
|
+
declare function addMessage(sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[], attachments?: StoredAttachment[]): Promise<void>;
|
|
2036
|
+
/**
|
|
2037
|
+
* Append a turn's terminalized activity records. Called ONCE per turn (from the
|
|
2038
|
+
* dispatch `finally`) — never per-delta.
|
|
2039
|
+
*/
|
|
2040
|
+
declare function appendActivity(sessionId: string, entries: ChatActivityRecord[]): Promise<void>;
|
|
2041
|
+
declare function listSessions(filters?: {
|
|
2042
|
+
agentId?: string;
|
|
2043
|
+
channel?: string;
|
|
2044
|
+
tenantId?: string;
|
|
2045
|
+
userId?: string;
|
|
2046
|
+
}, limit?: number): Promise<SessionSummary[]>;
|
|
2047
|
+
//#endregion
|
|
2048
|
+
//#region src/host-config.d.ts
|
|
2049
|
+
/**
|
|
2050
|
+
* Build the host config. Throws (→ non-zero exit from the CLI) when the chat
|
|
2051
|
+
* URL / api key can't resolve.
|
|
2052
|
+
*/
|
|
2053
|
+
declare function resolveHostConfig(): ClaudeCodeHostConfig;
|
|
2054
|
+
//#endregion
|
|
2055
|
+
export { type ActivityEmission, BRIDGE_ENV_VAR, type ChatActivityRecord, type ChatMessage, ClaudeCodeHost, type ClaudeCodeHostConfig, DEFAULT_ALLOWED_TOOLS, DEFAULT_PERMISSION_MODE, type DriverTurnResult, type EndEmission, type MapperEmission, type MapperState, type RunTurnParams, SCRUBBED_ANTHROPIC_ENV_VARS, type SessionData, SessionDriver, type SessionSummary, TURN_TIMEOUT_MS, type TextEmission, type ThinkingActivity, type ToolActivity, WARM_STREAMING_SUPPORTED, addMessage, appendActivity, buildChildEnv, collectTurnActivityRecords, createMapperState, createSession, getSession, listSessions, mapStreamEvent, mapStreamEvents, resolveHostConfig, saveSession, stripAgentPrefix };
|