@ah-oh/ao-workspaces-robin-client 0.0.1-preview.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # @ah-oh/ao-workspaces-robin-client
2
+
3
+ Framework-freier Kern für Clients der Robin-BFF (`apps/robin-api`): Wire-Typen,
4
+ ein `fetch`-Client mit Bearer-Token und Idempotency-Key, die Verfolgung eines
5
+ Runs bis zum Endzustand, Fehlertexte ohne technische Codes und das Lesen der
6
+ Antwortteile.
7
+
8
+ Genutzt von der Robin-App (`apps/robin-mobile`) und für die Typen von der
9
+ Angular-Library `@ah-oh/ao-workspaces-robin`. Spezifikation:
10
+ [`docs/features/robin-app/`](../../docs/features/robin-app/README.md),
11
+ Entscheidung RA-07.
12
+
13
+ ```ts
14
+ import { RobinBffClient, followRun } from '@ah-oh/ao-workspaces-robin-client';
15
+
16
+ const client = new RobinBffClient({
17
+ baseUrl: 'https://admin.ah-oh.com/robin-api',
18
+ getToken: () => token,
19
+ });
20
+ const follower = followRun(runId, { getRun: id => client.getRun(id), onUpdate, onEnd, onError });
21
+ ```
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@ah-oh/ao-workspaces-robin-client",
3
+ "version": "0.0.1-preview.39",
4
+ "description": "Framework-free client core for the Robin BFF: wire types, fetch client, run follower, error texts.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/ah-oh/ao-workspaces.git",
8
+ "directory": "libs/robin-client"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "type": "module",
14
+ "main": "./src/index.js",
15
+ "types": "./src/index.d.ts",
16
+ "sideEffects": false,
17
+ "dependencies": {
18
+ "tslib": "^2.3.0"
19
+ },
20
+ "module": "./src/index.js"
21
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './lib/types';
2
+ export * from './lib/parts';
3
+ export * from './lib/steps';
4
+ export * from './lib/sessions';
5
+ export * from './lib/confirmation';
6
+ export * from './lib/errors';
7
+ export * from './lib/bff-client';
8
+ export * from './lib/run-follower';
package/src/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Public API of @ah-oh/ao-workspaces-robin-client: the framework-free core
3
+ * shared by the Angular library and the Robin-App (docs/features/robin-app, RA-07).
4
+ */
5
+ export * from './lib/types';
6
+ export * from './lib/parts';
7
+ export * from './lib/steps';
8
+ export * from './lib/sessions';
9
+ export * from './lib/confirmation';
10
+ export * from './lib/errors';
11
+ export * from './lib/bff-client';
12
+ export * from './lib/run-follower';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/robin-client/src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { RobinConfirmation, RobinDeviceRegistration, RobinRunDetail, RobinSendMessageResponse, RobinSession, RobinSessionDetail, RobinSessionListResponse, RobinShortcut, RobinStoredAttachment, RobinSurfaceContext } from './types';
2
+ export interface RobinSendMessageRequest {
3
+ message: string;
4
+ context: {
5
+ surface: RobinSurfaceContext;
6
+ };
7
+ capabilityKey?: string;
8
+ input?: Record<string, unknown>;
9
+ }
10
+ export interface RobinAttachmentUpload {
11
+ name: string;
12
+ mimeType: string;
13
+ dataBase64: string;
14
+ }
15
+ export interface RobinBffClientOptions {
16
+ /** Address of the BFF including its prefix, e.g. `https://admin.ah-oh.com/robin-api`. */
17
+ baseUrl: string;
18
+ /** The user's token; sent as `Authorization: Bearer`. */
19
+ getToken: () => string | null | Promise<string | null>;
20
+ /** Sent as `X-Robin-App-Version`; the BFF may answer 426 below its minimum (RA-REQ-082). */
21
+ appVersion?: string;
22
+ timeoutMs?: number;
23
+ fetch?: typeof fetch;
24
+ }
25
+ /**
26
+ * Client of the Robin BFF on plain `fetch`, for clients without cookies such
27
+ * as the Robin-App (RA-07). Every call carries the bearer token and omits
28
+ * cookies, so no cookie can outrank the header (architektur.md, Anmeldung).
29
+ * Errors are thrown as {@link RobinHttpError}.
30
+ */
31
+ export declare class RobinBffClient {
32
+ private readonly options;
33
+ private readonly baseUrl;
34
+ private readonly timeoutMs;
35
+ private readonly fetchFn;
36
+ constructor(options: RobinBffClientOptions);
37
+ listSessions(): Promise<RobinSessionListResponse>;
38
+ getSession(sessionId: string): Promise<RobinSessionDetail>;
39
+ createSession(title: string | undefined, context: RobinSurfaceContext): Promise<RobinSession>;
40
+ /** The owner renames or pins a conversation (RS-26). */
41
+ updateSession(sessionId: string, changes: {
42
+ title?: string;
43
+ pinned?: boolean;
44
+ }): Promise<RobinSession>;
45
+ /** Removes a conversation from the owner's list; the platform keeps it for audit (RS-26). */
46
+ archiveSession(sessionId: string): Promise<RobinSession>;
47
+ /**
48
+ * Sends a message. A retry with the same `idempotencyKey` never starts a
49
+ * second run (RS-REQ-063, RA-REQ-023).
50
+ */
51
+ sendMessage(sessionId: string, request: RobinSendMessageRequest, idempotencyKey: string): Promise<RobinSendMessageResponse>;
52
+ getRun(runId: string): Promise<RobinRunDetail>;
53
+ cancelRun(runId: string): Promise<RobinRunDetail>;
54
+ listConfirmations(): Promise<RobinConfirmation[]>;
55
+ confirmDelegation(delegationId: string): Promise<RobinRunDetail>;
56
+ rejectDelegation(delegationId: string): Promise<RobinRunDetail>;
57
+ /** Scenarios bound to `app`; without `entityType` the ones bound to the app alone (RA-16). */
58
+ listShortcuts(app: string, entityType?: string): Promise<RobinShortcut[]>;
59
+ uploadAttachment(upload: RobinAttachmentUpload): Promise<RobinStoredAttachment>;
60
+ /** Registers the device's push token for the signed-in user (RA-05). */
61
+ registerDevice(installationId: string, registration: RobinDeviceRegistration): Promise<{
62
+ registered: true;
63
+ }>;
64
+ unregisterDevice(installationId: string): Promise<{
65
+ removed: boolean;
66
+ }>;
67
+ private request;
68
+ }
69
+ /** A fresh key per message; a retry of the same message reuses it (RA-REQ-023). */
70
+ export declare const createIdempotencyKey: () => string;
@@ -0,0 +1,142 @@
1
+ import { RobinHttpError } from './errors';
2
+ /**
3
+ * Client of the Robin BFF on plain `fetch`, for clients without cookies such
4
+ * as the Robin-App (RA-07). Every call carries the bearer token and omits
5
+ * cookies, so no cookie can outrank the header (architektur.md, Anmeldung).
6
+ * Errors are thrown as {@link RobinHttpError}.
7
+ */
8
+ export class RobinBffClient {
9
+ options;
10
+ baseUrl;
11
+ timeoutMs;
12
+ fetchFn;
13
+ constructor(options) {
14
+ this.options = options;
15
+ this.baseUrl = options.baseUrl.replace(/\/+$/, '');
16
+ this.timeoutMs = options.timeoutMs ?? 20_000;
17
+ this.fetchFn = options.fetch ?? ((input, init) => fetch(input, init));
18
+ }
19
+ listSessions() {
20
+ return this.request('GET', '/robin/sessions');
21
+ }
22
+ getSession(sessionId) {
23
+ return this.request('GET', `/robin/sessions/${encodeURIComponent(sessionId)}`);
24
+ }
25
+ createSession(title, context) {
26
+ return this.request('POST', '/robin/sessions', { title, context: { surface: context } });
27
+ }
28
+ /** The owner renames or pins a conversation (RS-26). */
29
+ updateSession(sessionId, changes) {
30
+ return this.request('PATCH', `/robin/sessions/${encodeURIComponent(sessionId)}`, changes);
31
+ }
32
+ /** Removes a conversation from the owner's list; the platform keeps it for audit (RS-26). */
33
+ archiveSession(sessionId) {
34
+ return this.request('DELETE', `/robin/sessions/${encodeURIComponent(sessionId)}`);
35
+ }
36
+ /**
37
+ * Sends a message. A retry with the same `idempotencyKey` never starts a
38
+ * second run (RS-REQ-063, RA-REQ-023).
39
+ */
40
+ sendMessage(sessionId, request, idempotencyKey) {
41
+ return this.request('POST', `/robin/sessions/${encodeURIComponent(sessionId)}/messages`, request, {
42
+ 'Idempotency-Key': idempotencyKey,
43
+ });
44
+ }
45
+ getRun(runId) {
46
+ return this.request('GET', `/robin/runs/${encodeURIComponent(runId)}`);
47
+ }
48
+ cancelRun(runId) {
49
+ return this.request('POST', `/robin/runs/${encodeURIComponent(runId)}/cancel`, {});
50
+ }
51
+ listConfirmations() {
52
+ return this.request('GET', '/robin/confirmations');
53
+ }
54
+ confirmDelegation(delegationId) {
55
+ return this.request('POST', `/robin/delegations/${encodeURIComponent(delegationId)}/confirm`, {});
56
+ }
57
+ rejectDelegation(delegationId) {
58
+ return this.request('POST', `/robin/delegations/${encodeURIComponent(delegationId)}/reject`, {});
59
+ }
60
+ /** Scenarios bound to `app`; without `entityType` the ones bound to the app alone (RA-16). */
61
+ listShortcuts(app, entityType) {
62
+ const query = new URLSearchParams({ app });
63
+ if (entityType)
64
+ query.set('entityType', entityType);
65
+ return this.request('GET', `/robin/shortcuts?${query.toString()}`);
66
+ }
67
+ uploadAttachment(upload) {
68
+ return this.request('POST', '/robin/attachments', upload);
69
+ }
70
+ /** Registers the device's push token for the signed-in user (RA-05). */
71
+ registerDevice(installationId, registration) {
72
+ return this.request('PUT', `/robin/devices/${encodeURIComponent(installationId)}`, registration);
73
+ }
74
+ unregisterDevice(installationId) {
75
+ return this.request('DELETE', `/robin/devices/${encodeURIComponent(installationId)}`);
76
+ }
77
+ async request(method, path, body, extraHeaders = {}) {
78
+ const token = await this.options.getToken();
79
+ const headers = { Accept: 'application/json', ...extraHeaders };
80
+ if (token)
81
+ headers['Authorization'] = `Bearer ${token}`;
82
+ if (this.options.appVersion)
83
+ headers['X-Robin-App-Version'] = this.options.appVersion;
84
+ if (body !== undefined)
85
+ headers['Content-Type'] = 'application/json';
86
+ const controller = new AbortController();
87
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
88
+ let response;
89
+ try {
90
+ response = await this.fetchFn(`${this.baseUrl}${path}`, {
91
+ method,
92
+ headers,
93
+ body: body === undefined ? undefined : JSON.stringify(body),
94
+ credentials: 'omit',
95
+ signal: controller.signal,
96
+ });
97
+ }
98
+ catch (error) {
99
+ throw new RobinHttpError(0, error instanceof Error ? error.message : 'Network request failed');
100
+ }
101
+ finally {
102
+ clearTimeout(timer);
103
+ }
104
+ const payload = await readBody(response);
105
+ if (!response.ok) {
106
+ throw new RobinHttpError(response.status, messageOf(payload) ?? `HTTP ${response.status}`, payload);
107
+ }
108
+ return payload;
109
+ }
110
+ }
111
+ const readBody = async (response) => {
112
+ const text = await response.text().catch(() => '');
113
+ if (!text)
114
+ return undefined;
115
+ try {
116
+ return JSON.parse(text);
117
+ }
118
+ catch {
119
+ return text;
120
+ }
121
+ };
122
+ const messageOf = (payload) => {
123
+ if (payload && typeof payload === 'object' && 'message' in payload) {
124
+ const message = payload.message;
125
+ if (typeof message === 'string')
126
+ return message;
127
+ if (Array.isArray(message))
128
+ return message.join(', ');
129
+ }
130
+ return undefined;
131
+ };
132
+ /** A fresh key per message; a retry of the same message reuses it (RA-REQ-023). */
133
+ export const createIdempotencyKey = () => {
134
+ const cryptoApi = globalThis.crypto;
135
+ if (cryptoApi?.randomUUID)
136
+ return cryptoApi.randomUUID();
137
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, char => {
138
+ const random = (Math.random() * 16) | 0;
139
+ return (char === 'x' ? random : (random & 0x3) | 0x8).toString(16);
140
+ });
141
+ };
142
+ //# sourceMappingURL=bff-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bff-client.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/bff-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { RobinConfirmation } from './types';
2
+ /** An open confirmation prepared for a card (RA-REQ-040, RA-REQ-041): names, never raw keys. */
3
+ export interface RobinConfirmationView {
4
+ delegationId: string;
5
+ /** What Robin wants to do, by the capability's name. */
6
+ action: string;
7
+ /** The record the call changes, by name. */
8
+ record?: string;
9
+ /** The change in one sentence, in Robin's words. */
10
+ change?: string;
11
+ risk: 'LOW' | 'MEDIUM' | 'HIGH';
12
+ riskLabel: string;
13
+ effects: string[];
14
+ /** Input with readable labels; technical ids and the context stay hidden. */
15
+ inputs: {
16
+ key: string;
17
+ label: string;
18
+ value: string;
19
+ }[];
20
+ /** Roles that may decide, by name. */
21
+ roles: string[];
22
+ /** `false` when the caller lacks the role; the card then names who can decide (RA-REQ-041). */
23
+ canDecide: boolean;
24
+ }
25
+ export declare const describeConfirmation: (confirmation: RobinConfirmation) => RobinConfirmationView;
@@ -0,0 +1,65 @@
1
+ const ROLE_LABELS = {
2
+ 'super-admin': 'Super-Admin',
3
+ 'company-admin': 'Admin',
4
+ owner: 'Inhaber',
5
+ admin: 'Admin',
6
+ user: 'Nutzer',
7
+ };
8
+ const MAX_INPUTS = 12;
9
+ const MAX_VALUE = 160;
10
+ export const describeConfirmation = (confirmation) => {
11
+ const summary = confirmation.inputSummary;
12
+ const policy = confirmation.policySnapshot;
13
+ const { input = {}, labels = {}, hidden = [] } = summary;
14
+ const roleKeys = confirmation.requiredRoles ?? policy.confirmationRoles ?? [];
15
+ return {
16
+ delegationId: confirmation.delegationId,
17
+ action: summary.capabilityName ?? summary.capabilityKey ?? 'Aktion',
18
+ record: summary.record?.label || undefined,
19
+ change: summary.change?.trim() || undefined,
20
+ risk: confirmation.riskLevel,
21
+ riskLabel: riskLabel(confirmation.riskLevel),
22
+ effects: [
23
+ policy.writes ? 'ändert Daten' : null,
24
+ policy.external ? 'extern' : null,
25
+ policy.customerVisible ? 'für Kunden sichtbar' : null,
26
+ policy.destructive ? 'löscht Daten' : null,
27
+ policy.securitySensitive ? 'sicherheitsrelevant' : null,
28
+ policy.publishing ? 'veröffentlicht' : null,
29
+ policy.reversible === false ? 'nicht umkehrbar' : null,
30
+ ].filter((flag) => Boolean(flag)),
31
+ inputs: Object.entries(input)
32
+ .filter(([key]) => key !== 'context' && !hidden.includes(key))
33
+ .slice(0, MAX_INPUTS)
34
+ .map(([key, value]) => ({ key, label: labels[key] ?? key, value: formatValue(value) })),
35
+ // The platform's service account is nobody the user can ask.
36
+ roles: [
37
+ ...new Set(roleKeys.filter(role => role !== 'service').map(role => ROLE_LABELS[role] ?? role)),
38
+ ],
39
+ canDecide: confirmation.canDecide !== false,
40
+ };
41
+ };
42
+ const riskLabel = (risk) => {
43
+ switch (risk) {
44
+ case 'HIGH':
45
+ return 'Hohes Risiko';
46
+ case 'MEDIUM':
47
+ return 'Mittleres Risiko';
48
+ default:
49
+ return 'Geringes Risiko';
50
+ }
51
+ };
52
+ const formatValue = (value) => {
53
+ if (value === null || value === undefined)
54
+ return '–';
55
+ if (typeof value === 'string')
56
+ return value.length > MAX_VALUE ? `${value.slice(0, MAX_VALUE)}…` : value;
57
+ if (typeof value === 'boolean')
58
+ return value ? 'ja' : 'nein';
59
+ if (typeof value === 'number')
60
+ return String(value);
61
+ if (Array.isArray(value))
62
+ return value.length === 1 ? '1 Eintrag' : `${value.length} Einträge`;
63
+ return JSON.stringify(value).slice(0, MAX_VALUE);
64
+ };
65
+ //# sourceMappingURL=confirmation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirmation.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/confirmation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { RobinRunDetail } from './types';
2
+ /**
3
+ * A failed call to the BFF. `status` 0 means the BFF was not reached at all
4
+ * (no network, timeout); `body` is the parsed JSON answer, if any.
5
+ */
6
+ export declare class RobinHttpError extends Error {
7
+ readonly status: number;
8
+ readonly body?: unknown | undefined;
9
+ constructor(status: number, message: string, body?: unknown | undefined);
10
+ }
11
+ export type RobinErrorKind = 'offline' | 'unauthorized' | 'forbidden' | 'busy' | 'rate_limited' | 'quota_exhausted' | 'upgrade_required' | 'too_large' | 'unknown';
12
+ /** What the user reads: never a technical code, always what to do instead (RA-REQ-030). */
13
+ export interface RobinErrorDescription {
14
+ kind: RobinErrorKind;
15
+ message: string;
16
+ }
17
+ /** Code of the credits feature for an empty company account (Credits CR-REQ-025). */
18
+ export declare const CREDITS_EXHAUSTED = "CREDITS_EXHAUSTED";
19
+ /**
20
+ * Without purchases in the app, the message names the company's administrator
21
+ * and links nowhere (RA-11, RA-REQ-081).
22
+ */
23
+ export declare const QUOTA_EXHAUSTED_MESSAGE = "KI-Aktionen stehen zurzeit nicht zur Verf\u00FCgung: Das Kontingent deiner Company ist aufgebraucht. Der Administrator deiner Company kann es erh\u00F6hen.";
24
+ /** Maps transport and platform errors to what the user can do (RA-REQ-030, RS-REQ-084). */
25
+ export declare const describeRobinError: (error: unknown) => RobinErrorDescription;
26
+ /** Why a run ended without an answer, readable and without codes. */
27
+ export declare const describeRunFailure: (run: Pick<RobinRunDetail, "errorMessage">) => string;
@@ -0,0 +1,83 @@
1
+ import { readableRunError } from './steps';
2
+ /**
3
+ * A failed call to the BFF. `status` 0 means the BFF was not reached at all
4
+ * (no network, timeout); `body` is the parsed JSON answer, if any.
5
+ */
6
+ export class RobinHttpError extends Error {
7
+ status;
8
+ body;
9
+ constructor(status, message, body) {
10
+ super(message);
11
+ this.status = status;
12
+ this.body = body;
13
+ this.name = 'RobinHttpError';
14
+ }
15
+ }
16
+ /** Code of the credits feature for an empty company account (Credits CR-REQ-025). */
17
+ export const CREDITS_EXHAUSTED = 'CREDITS_EXHAUSTED';
18
+ /**
19
+ * Without purchases in the app, the message names the company's administrator
20
+ * and links nowhere (RA-11, RA-REQ-081).
21
+ */
22
+ export const QUOTA_EXHAUSTED_MESSAGE = 'KI-Aktionen stehen zurzeit nicht zur Verfügung: Das Kontingent deiner Company ist aufgebraucht. Der Administrator deiner Company kann es erhöhen.';
23
+ const MESSAGES = {
24
+ offline: 'Robin ist gerade nicht erreichbar. Deine Eingabe bleibt stehen; sende sie erneut, sobald du wieder Netz hast.',
25
+ unauthorized: 'Deine Anmeldung ist abgelaufen. Melde dich neu an; deine Eingabe bleibt erhalten.',
26
+ busy: 'Robin arbeitet noch an der letzten Aufgabe. Warte, bis sie fertig ist, oder brich sie ab.',
27
+ rate_limited: 'Gerade kommen zu viele Anfragen an. Versuche es in einer Minute noch einmal.',
28
+ upgrade_required: 'Diese Version der Robin-App wird nicht mehr unterstützt. Aktualisiere die App im App Store oder bei Google Play.',
29
+ too_large: 'Die Datei ist zu groß. Wähle eine kleinere Datei oder ein Foto.',
30
+ };
31
+ /** Maps transport and platform errors to what the user can do (RA-REQ-030, RS-REQ-084). */
32
+ export const describeRobinError = (error) => {
33
+ if (!(error instanceof RobinHttpError)) {
34
+ return { kind: 'unknown', message: 'Etwas ist schiefgegangen. Versuche es noch einmal.' };
35
+ }
36
+ const detail = detailOf(error.body);
37
+ if (detail.includes(CREDITS_EXHAUSTED) || error.status === 402) {
38
+ return { kind: 'quota_exhausted', message: QUOTA_EXHAUSTED_MESSAGE };
39
+ }
40
+ switch (error.status) {
41
+ case 0:
42
+ case 502:
43
+ case 503:
44
+ case 504:
45
+ return { kind: 'offline', message: MESSAGES.offline };
46
+ case 401:
47
+ return { kind: 'unauthorized', message: MESSAGES.unauthorized };
48
+ case 403:
49
+ return {
50
+ kind: 'forbidden',
51
+ message: 'Dafür fehlt dir die Berechtigung. Wende dich an den Administrator deiner Company.',
52
+ };
53
+ case 409:
54
+ return { kind: 'busy', message: MESSAGES.busy };
55
+ case 413:
56
+ return { kind: 'too_large', message: MESSAGES.too_large };
57
+ case 426:
58
+ return { kind: 'upgrade_required', message: MESSAGES.upgrade_required };
59
+ case 429:
60
+ return { kind: 'rate_limited', message: MESSAGES.rate_limited };
61
+ default:
62
+ return {
63
+ kind: 'unknown',
64
+ message: 'Robin konnte die Anfrage nicht ausführen. Versuche es noch einmal.',
65
+ };
66
+ }
67
+ };
68
+ /** Why a run ended without an answer, readable and without codes. */
69
+ export const describeRunFailure = (run) => {
70
+ const message = run.errorMessage ?? '';
71
+ if (message.includes(CREDITS_EXHAUSTED))
72
+ return QUOTA_EXHAUSTED_MESSAGE;
73
+ return `Die Aufgabe konnte nicht abgeschlossen werden: ${readableRunError(message)}`;
74
+ };
75
+ const detailOf = (body) => {
76
+ if (!body || typeof body !== 'object')
77
+ return typeof body === 'string' ? body : '';
78
+ const record = body;
79
+ return [record['code'], record['error'], record['message']]
80
+ .map(value => (Array.isArray(value) ? value.join(' ') : typeof value === 'string' ? value : ''))
81
+ .join(' ');
82
+ };
83
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { RobinMessagePart, RobinPlatformMessage, RobinTextPart } from './types';
2
+ /** Reads `content.parts` of a platform message; anything unknown becomes text (RS-REQ-020). */
3
+ export declare const toMessageParts: (message: Pick<RobinPlatformMessage, "contentText" | "content">) => RobinMessagePart[];
4
+ /**
5
+ * Parts for a client without page context (the Robin-App): proposals, actions
6
+ * and summaries need a page, so they are shown as text instead of being
7
+ * dropped (RA-REQ-025).
8
+ */
9
+ export declare const toTextParts: (parts: readonly RobinMessagePart[]) => RobinTextPart[];
10
+ /** A message of a conversation as a chat shows it. */
11
+ export interface RobinChatMessage {
12
+ id: string;
13
+ role: 'user' | 'assistant';
14
+ parts: RobinMessagePart[];
15
+ createdAt: string;
16
+ runId?: string;
17
+ }
18
+ /** User and assistant messages of a conversation in their order; tool and system messages stay hidden. */
19
+ export declare const toChatMessages: (messages: readonly RobinPlatformMessage[]) => RobinChatMessage[];
@@ -0,0 +1,110 @@
1
+ /** Reads `content.parts` of a platform message; anything unknown becomes text (RS-REQ-020). */
2
+ export const toMessageParts = (message) => {
3
+ const content = message.content;
4
+ const rawParts = content && typeof content === 'object' && Array.isArray(content.parts)
5
+ ? content.parts
6
+ : null;
7
+ if (!rawParts) {
8
+ const text = message.contentText?.trim();
9
+ return text ? [{ kind: 'text', markdown: text }] : [];
10
+ }
11
+ const parts = [];
12
+ for (const raw of rawParts) {
13
+ const part = normalizePart(raw);
14
+ if (part)
15
+ parts.push(part);
16
+ }
17
+ if (parts.length === 0 && message.contentText?.trim()) {
18
+ parts.push({ kind: 'text', markdown: message.contentText.trim() });
19
+ }
20
+ return parts;
21
+ };
22
+ const normalizePart = (raw) => {
23
+ if (!raw || typeof raw !== 'object')
24
+ return null;
25
+ const part = raw;
26
+ switch (part['kind']) {
27
+ case 'text':
28
+ return typeof part['markdown'] === 'string' && part['markdown'].trim()
29
+ ? { kind: 'text', markdown: part['markdown'] }
30
+ : null;
31
+ case 'proposal': {
32
+ const changes = Array.isArray(part['changes'])
33
+ ? part['changes'].filter((change) => !!change &&
34
+ typeof change === 'object' &&
35
+ typeof change.fieldId === 'string' &&
36
+ typeof change.after === 'string')
37
+ : [];
38
+ if (changes.length === 0)
39
+ return null;
40
+ return {
41
+ kind: 'proposal',
42
+ title: typeof part['title'] === 'string' ? part['title'] : undefined,
43
+ rationale: typeof part['rationale'] === 'string' ? part['rationale'] : undefined,
44
+ changes: changes.map(change => ({
45
+ fieldId: change.fieldId,
46
+ before: typeof change.before === 'string' ? change.before : '',
47
+ after: change.after,
48
+ })),
49
+ };
50
+ }
51
+ case 'action':
52
+ return typeof part['actionId'] === 'string'
53
+ ? {
54
+ kind: 'action',
55
+ actionId: part['actionId'],
56
+ label: typeof part['label'] === 'string' ? part['label'] : part['actionId'],
57
+ reason: typeof part['reason'] === 'string' ? part['reason'] : undefined,
58
+ }
59
+ : null;
60
+ case 'summary':
61
+ return typeof part['text'] === 'string'
62
+ ? {
63
+ kind: 'summary',
64
+ text: part['text'],
65
+ sources: Array.isArray(part['sources'])
66
+ ? part['sources']
67
+ : [],
68
+ }
69
+ : null;
70
+ default:
71
+ return { kind: 'text', markdown: '```json\n' + JSON.stringify(raw, null, 2) + '\n```' };
72
+ }
73
+ };
74
+ /**
75
+ * Parts for a client without page context (the Robin-App): proposals, actions
76
+ * and summaries need a page, so they are shown as text instead of being
77
+ * dropped (RA-REQ-025).
78
+ */
79
+ export const toTextParts = (parts) => parts.map((part) => {
80
+ switch (part.kind) {
81
+ case 'text':
82
+ return part;
83
+ case 'summary':
84
+ return { kind: 'text', markdown: part.text };
85
+ case 'action':
86
+ return {
87
+ kind: 'text',
88
+ markdown: part.reason ? `**${part.label}**\n\n${part.reason}` : `**${part.label}**`,
89
+ };
90
+ case 'proposal': {
91
+ const lines = part.changes.map(change => change.before
92
+ ? `- ${change.fieldId}: ${change.before} → ${change.after}`
93
+ : `- ${change.fieldId}: ${change.after}`);
94
+ const heading = part.title ? `**${part.title}**\n\n` : '';
95
+ const rationale = part.rationale ? `${part.rationale}\n\n` : '';
96
+ return { kind: 'text', markdown: `${heading}${rationale}${lines.join('\n')}` };
97
+ }
98
+ }
99
+ });
100
+ /** User and assistant messages of a conversation in their order; tool and system messages stay hidden. */
101
+ export const toChatMessages = (messages) => messages
102
+ .filter(message => message.role === 'USER' || message.role === 'ASSISTANT')
103
+ .map(message => ({
104
+ id: message.id,
105
+ role: message.role === 'USER' ? 'user' : 'assistant',
106
+ parts: toMessageParts(message),
107
+ createdAt: message.createdAt,
108
+ runId: message.runId ?? undefined,
109
+ }));
110
+ //# sourceMappingURL=parts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/parts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { RobinRunDetail } from './types';
2
+ export interface RunFollowerOptions {
3
+ getRun: (runId: string) => Promise<RobinRunDetail>;
4
+ /** Every fresh state of the run, including the last one. */
5
+ onUpdate: (run: RobinRunDetail) => void;
6
+ /** The run reached `SUCCESS`, `FAILED` or `CANCELLED`; polling has stopped. */
7
+ onEnd: (run: RobinRunDetail) => void;
8
+ /** A poll failed. `final` is true when the follower gave up. */
9
+ onError: (error: unknown, final: boolean) => void;
10
+ /** Interval between polls; 2 seconds in the app (architektur.md, Chat). */
11
+ intervalMs?: number;
12
+ /** Offline errors in a row before giving up; other errors end at once. */
13
+ maxOfflineRetries?: number;
14
+ }
15
+ export interface RunFollower {
16
+ readonly runId: string;
17
+ stop(): void;
18
+ readonly stopped: boolean;
19
+ }
20
+ /**
21
+ * Polls a run until it ends (RS-REQ-030, RA-REQ-024). The caller stops it
22
+ * when the app leaves the foreground; the notification takes over there
23
+ * (architektur.md, Chat step 4).
24
+ */
25
+ export declare const followRun: (runId: string, options: RunFollowerOptions) => RunFollower;
@@ -0,0 +1,64 @@
1
+ import { describeRobinError } from './errors';
2
+ import { isTerminalRunStatus } from './types';
3
+ /**
4
+ * Polls a run until it ends (RS-REQ-030, RA-REQ-024). The caller stops it
5
+ * when the app leaves the foreground; the notification takes over there
6
+ * (architektur.md, Chat step 4).
7
+ */
8
+ export const followRun = (runId, options) => {
9
+ const intervalMs = options.intervalMs ?? 2_000;
10
+ const maxOfflineRetries = options.maxOfflineRetries ?? 5;
11
+ let timer = null;
12
+ let stopped = false;
13
+ let offlineErrors = 0;
14
+ const stop = () => {
15
+ stopped = true;
16
+ if (timer)
17
+ clearTimeout(timer);
18
+ timer = null;
19
+ };
20
+ const schedule = () => {
21
+ if (!stopped)
22
+ timer = setTimeout(() => void poll(), intervalMs);
23
+ };
24
+ const poll = async () => {
25
+ if (stopped)
26
+ return;
27
+ let run;
28
+ try {
29
+ run = await options.getRun(runId);
30
+ }
31
+ catch (error) {
32
+ if (stopped)
33
+ return;
34
+ const offline = describeRobinError(error).kind === 'offline';
35
+ offlineErrors = offline ? offlineErrors + 1 : Number.POSITIVE_INFINITY;
36
+ const final = offlineErrors > maxOfflineRetries;
37
+ options.onError(error, final);
38
+ if (final)
39
+ stop();
40
+ else
41
+ schedule();
42
+ return;
43
+ }
44
+ if (stopped)
45
+ return;
46
+ offlineErrors = 0;
47
+ options.onUpdate(run);
48
+ if (isTerminalRunStatus(run.status)) {
49
+ stop();
50
+ options.onEnd(run);
51
+ return;
52
+ }
53
+ schedule();
54
+ };
55
+ void poll();
56
+ return {
57
+ runId,
58
+ stop,
59
+ get stopped() {
60
+ return stopped;
61
+ },
62
+ };
63
+ };
64
+ //# sourceMappingURL=run-follower.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-follower.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/run-follower.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAsB9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,OAA2B,EAAe,EAAE;IACnF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC;IAC/C,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,CAAC,CAAC;IACzD,IAAI,KAAK,GAAyC,IAAI,CAAC;IACvD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,GAAG,IAAI,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,IAAI,CAAC,OAAO;YAAE,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;IAClE,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACrC,IAAI,OAAO;YAAE,OAAO;QACpB,IAAI,GAAmB,CAAC;QACxB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO;gBAAE,OAAO;YACpB,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC;YAC7D,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;YACvE,MAAM,KAAK,GAAG,aAAa,GAAG,iBAAiB,CAAC;YAChD,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC9B,IAAI,KAAK;gBAAE,IAAI,EAAE,CAAC;;gBACb,QAAQ,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,OAAO;YAAE,OAAO;QACpB,aAAa,GAAG,CAAC,CAAC;QAClB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,IAAI,EAAE,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QACD,QAAQ,EAAE,CAAC;IACb,CAAC,CAAC;IAEF,KAAK,IAAI,EAAE,CAAC;IACZ,OAAO;QACL,KAAK;QACL,IAAI;QACJ,IAAI,OAAO;YACT,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { RobinConfirmation, RobinSession } from './types';
2
+ /** First line of a message, cut at a word near {@link MAX_SESSION_TITLE} characters (RS-REQ-097, RA-REQ-022). */
3
+ export declare const sessionTitleOf: (message: string) => string;
4
+ /** Name of a conversation in a list; a conversation without title is still recognisable. */
5
+ export declare const sessionLabel: (session: Pick<RobinSession, "title">) => string;
6
+ /** Pinned conversations first (last pinned on top), the rest by last activity (RA-REQ-020). */
7
+ export declare const sortSessions: <T extends RobinSession>(sessions: readonly T[]) => T[];
8
+ /** Search by title, case- and accent-insensitive (RA-REQ-020). */
9
+ export declare const filterSessions: <T extends RobinSession>(sessions: readonly T[], query: string) => T[];
10
+ /** Conversations with an open confirmation, to flag them in the list (RA-REQ-042). */
11
+ export declare const sessionsWaitingForConfirmation: (confirmations: readonly RobinConfirmation[]) => Set<string>;
@@ -0,0 +1,40 @@
1
+ const MAX_SESSION_TITLE = 60;
2
+ /** First line of a message, cut at a word near {@link MAX_SESSION_TITLE} characters (RS-REQ-097, RA-REQ-022). */
3
+ export const sessionTitleOf = (message) => {
4
+ const line = message.trim().split('\n')[0]?.trim() ?? '';
5
+ if (line.length <= MAX_SESSION_TITLE)
6
+ return line;
7
+ const cut = line.slice(0, MAX_SESSION_TITLE);
8
+ const space = cut.lastIndexOf(' ');
9
+ return `${(space > MAX_SESSION_TITLE / 2 ? cut.slice(0, space) : cut).replace(/\s+$/, '')} …`;
10
+ };
11
+ /** Name of a conversation in a list; a conversation without title is still recognisable. */
12
+ export const sessionLabel = (session) => session.title?.trim() || 'Neue Unterhaltung';
13
+ /** Pinned conversations first (last pinned on top), the rest by last activity (RA-REQ-020). */
14
+ export const sortSessions = (sessions) => {
15
+ const activity = (session) => session.lastMessageAt ?? session.createdAt;
16
+ const pinned = sessions
17
+ .filter(session => session.pinnedAt)
18
+ .sort((a, b) => String(b.pinnedAt).localeCompare(String(a.pinnedAt)));
19
+ const rest = sessions
20
+ .filter(session => !session.pinnedAt)
21
+ .sort((a, b) => activity(b).localeCompare(activity(a)));
22
+ return [...pinned, ...rest];
23
+ };
24
+ /** Search by title, case- and accent-insensitive (RA-REQ-020). */
25
+ export const filterSessions = (sessions, query) => {
26
+ const wanted = normalize(query);
27
+ if (!wanted)
28
+ return [...sessions];
29
+ return sessions.filter(session => normalize(sessionLabel(session)).includes(wanted));
30
+ };
31
+ /** Conversations with an open confirmation, to flag them in the list (RA-REQ-042). */
32
+ export const sessionsWaitingForConfirmation = (confirmations) => new Set(confirmations
33
+ .filter(confirmation => confirmation.status === 'PENDING' && confirmation.sessionId)
34
+ .map(confirmation => confirmation.sessionId));
35
+ const normalize = (value) => value
36
+ .normalize('NFD')
37
+ .replace(/[\u0300-\u036f]/g, '')
38
+ .toLowerCase()
39
+ .trim();
40
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { RobinRunDetail, RobinRunStep, RobinShortcut } from './types';
2
+ /**
3
+ * A failed step reads `CODE: message (details)` (tool errors); the user gets
4
+ * the message only (RS-REQ-084).
5
+ */
6
+ export declare const readableRunError: (message: string | null | undefined) => string;
7
+ /** Progress of the run for the panel (RS-REQ-030) from its events and delegations. */
8
+ export declare const deriveSteps: (run: RobinRunDetail | null, shortcuts: readonly RobinShortcut[]) => RobinRunStep[];
@@ -0,0 +1,99 @@
1
+ /**
2
+ * A failed step reads `CODE: message (details)` (tool errors); the user gets
3
+ * the message only (RS-REQ-084).
4
+ */
5
+ export const readableRunError = (message) => {
6
+ const text = (message ?? '').trim();
7
+ if (!text)
8
+ return 'unbekannter Fehler';
9
+ const readable = text
10
+ .replace(/^(?:[A-Z][A-Z0-9_]{2,}:\s+)+/, '')
11
+ .replace(/\s+\((?:\{.*\}|\[.*\])\)$/s, '')
12
+ .trim();
13
+ return readable || text;
14
+ };
15
+ /** Progress of the run for the panel (RS-REQ-030) from its events and delegations. */
16
+ export const deriveSteps = (run, shortcuts) => {
17
+ if (!run)
18
+ return [];
19
+ const names = new Map();
20
+ for (const shortcut of shortcuts) {
21
+ for (const step of shortcut.steps)
22
+ names.set(step.capabilityKey, step.name);
23
+ }
24
+ const steps = new Map();
25
+ const delegationKeys = new Map();
26
+ for (const event of run.events) {
27
+ const payload = event.payload ?? {};
28
+ const key = typeof payload['capabilityKey'] === 'string' ? payload['capabilityKey'] : undefined;
29
+ const delegationId = typeof payload['delegationId'] === 'string' ? payload['delegationId'] : undefined;
30
+ const name = typeof payload['capabilityName'] === 'string' ? payload['capabilityName'] : undefined;
31
+ if (key && name && !names.has(key))
32
+ names.set(key, name);
33
+ switch (event.type) {
34
+ case 'capability_selected':
35
+ if (key && delegationId)
36
+ delegationKeys.set(delegationId, key);
37
+ if (key && !steps.has(key)) {
38
+ steps.set(key, { key, label: names.get(key) ?? prettify(key), state: 'planned' });
39
+ }
40
+ break;
41
+ case 'confirmation_required':
42
+ if (key) {
43
+ if (delegationId)
44
+ delegationKeys.set(delegationId, key);
45
+ setState(steps, key, 'waiting_confirmation', names);
46
+ }
47
+ break;
48
+ case 'delegation_started': {
49
+ const stepKey = key ??
50
+ (delegationId ? delegationKeys.get(delegationId) : undefined) ??
51
+ lastPlanned(steps);
52
+ if (stepKey) {
53
+ if (delegationId)
54
+ delegationKeys.set(delegationId, stepKey);
55
+ setState(steps, stepKey, 'running', names);
56
+ }
57
+ break;
58
+ }
59
+ case 'child_completed': {
60
+ const stepKey = delegationId ? delegationKeys.get(delegationId) : undefined;
61
+ if (stepKey)
62
+ setState(steps, stepKey, 'done', names);
63
+ break;
64
+ }
65
+ case 'run_failed':
66
+ for (const step of steps.values()) {
67
+ if (step.state === 'running' || step.state === 'waiting_confirmation')
68
+ step.state = 'failed';
69
+ }
70
+ break;
71
+ default:
72
+ break;
73
+ }
74
+ }
75
+ return [...steps.values()];
76
+ };
77
+ const setState = (steps, key, state, names) => {
78
+ const step = steps.get(key);
79
+ if (step) {
80
+ step.state = state;
81
+ }
82
+ else {
83
+ steps.set(key, { key, label: names.get(key) ?? prettify(key), state });
84
+ }
85
+ };
86
+ const lastPlanned = (steps) => {
87
+ let candidate;
88
+ for (const step of steps.values()) {
89
+ if (step.state === 'planned')
90
+ candidate = step.key;
91
+ }
92
+ return candidate;
93
+ };
94
+ const prettify = (key) => key
95
+ .split(/[._-]+/)
96
+ .filter(Boolean)
97
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
98
+ .join(' ');
99
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/steps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Wire types of the Robin BFF (`apps/robin-api`) shared by the Angular library
3
+ * `libs/robin` and the Robin-App `apps/robin-mobile` (docs/features/robin-app,
4
+ * RA-07). They mirror the shared types of the AI platform
5
+ * (`@ai/shared/types`, `agents-robin*.types.ts`); the platform is the source
6
+ * of truth, the BFF passes the shapes through unchanged.
7
+ */
8
+ export type RobinSurfaceFieldType = 'text' | 'multiline' | 'number' | 'select' | 'date';
9
+ /** How the library shows a field value in previews and cards; never sent to Robin. */
10
+ export type RobinFieldFormat = 'text' | 'html';
11
+ export interface RobinSurfaceEntity {
12
+ type: string;
13
+ id: string;
14
+ label: string;
15
+ version?: string;
16
+ }
17
+ export interface RobinSurfaceFocus {
18
+ kind: 'field' | 'section';
19
+ id: string;
20
+ }
21
+ /** A value a `select` field accepts and the name shown for it in previews and cards. */
22
+ export interface RobinFieldOption {
23
+ value: string;
24
+ label?: string;
25
+ }
26
+ export interface RobinSurfaceField {
27
+ id: string;
28
+ label: string;
29
+ type?: RobinSurfaceFieldType;
30
+ value?: string;
31
+ sectionId?: string;
32
+ /** Allowed values of a `select` field with the names the user sees (RS-23). */
33
+ options?: RobinFieldOption[];
34
+ /** Several options at once, as a comma-separated list of values (RS-23). */
35
+ multiple?: boolean;
36
+ /** What Robin should know about the field (format, limits); never shown. */
37
+ hint?: string;
38
+ }
39
+ export interface RobinSurfaceSection {
40
+ id: string;
41
+ type: string;
42
+ label: string;
43
+ }
44
+ export interface RobinActionEffect {
45
+ writes?: boolean;
46
+ external?: boolean;
47
+ customerVisible?: boolean;
48
+ destructive?: boolean;
49
+ securitySensitive?: boolean;
50
+ publishing?: boolean;
51
+ reversible?: boolean;
52
+ }
53
+ export interface RobinSurfaceAction {
54
+ id: string;
55
+ label: string;
56
+ effect?: RobinActionEffect;
57
+ }
58
+ export interface RobinSurfaceAttachment {
59
+ id: string;
60
+ name: string;
61
+ mimeType: string;
62
+ url: string;
63
+ }
64
+ export interface RobinSurfaceContext {
65
+ app: string;
66
+ route?: string;
67
+ capabilityKeys?: string[];
68
+ scenarioKey?: string;
69
+ entity?: RobinSurfaceEntity;
70
+ focus?: RobinSurfaceFocus;
71
+ fields?: RobinSurfaceField[];
72
+ sections?: RobinSurfaceSection[];
73
+ actions?: RobinSurfaceAction[];
74
+ attachments?: RobinSurfaceAttachment[];
75
+ }
76
+ /** A quick action on a field: calls `capabilityKey` directly with an instruction (RS-14). */
77
+ export interface RobinQuickAction {
78
+ id: string;
79
+ label: string;
80
+ instruction: string;
81
+ capabilityKey?: string;
82
+ }
83
+ export interface RobinProposalChange {
84
+ fieldId: string;
85
+ before: string;
86
+ after: string;
87
+ }
88
+ export interface RobinSummarySource {
89
+ delegationId: string;
90
+ capabilityKey: string;
91
+ label: string;
92
+ }
93
+ export interface RobinTextPart {
94
+ kind: 'text';
95
+ markdown: string;
96
+ }
97
+ export interface RobinProposalPart {
98
+ kind: 'proposal';
99
+ title?: string;
100
+ rationale?: string;
101
+ changes: RobinProposalChange[];
102
+ }
103
+ export interface RobinActionPart {
104
+ kind: 'action';
105
+ actionId: string;
106
+ label: string;
107
+ reason?: string;
108
+ }
109
+ export interface RobinSummaryPart {
110
+ kind: 'summary';
111
+ text: string;
112
+ sources: RobinSummarySource[];
113
+ }
114
+ export type RobinMessagePart = RobinTextPart | RobinProposalPart | RobinActionPart | RobinSummaryPart;
115
+ export type RobinRunStatus = 'QUEUED' | 'RUNNING' | 'WAITING_FOR_CHILD' | 'WAITING_FOR_CONFIRMATION' | 'NEEDS_INPUT' | 'RESUMING' | 'SUCCESS' | 'FAILED' | 'CANCELLED';
116
+ export declare const TERMINAL_RUN_STATUSES: readonly RobinRunStatus[];
117
+ export declare const isTerminalRunStatus: (status: RobinRunStatus | string) => boolean;
118
+ export interface RobinRunEvent {
119
+ id: string;
120
+ seq: number;
121
+ type: string;
122
+ payload?: Record<string, unknown> | null;
123
+ createdAt: string;
124
+ }
125
+ export interface RobinDelegation {
126
+ id: string;
127
+ status: string;
128
+ capabilityId?: string | null;
129
+ riskLevel?: string | null;
130
+ errorMessage?: string | null;
131
+ /** Effective policy of the delegated capability; `writes` marks a data change. */
132
+ effectPolicy?: RobinActionEffect | null;
133
+ }
134
+ /** A finished run changed data of the entity it was started on (host apps reload). */
135
+ export interface RobinEntityDataChange {
136
+ runId: string;
137
+ entity: RobinSurfaceEntity;
138
+ /** Ids of the successful delegations with a writing effect. */
139
+ delegationIds: string[];
140
+ }
141
+ export interface RobinPlatformMessage {
142
+ id: string;
143
+ role: 'USER' | 'ASSISTANT' | 'TOOL' | 'SYSTEM';
144
+ contentText?: string | null;
145
+ content?: unknown;
146
+ runId?: string | null;
147
+ createdAt: string;
148
+ }
149
+ export interface RobinRunDetail {
150
+ id: string;
151
+ status: RobinRunStatus;
152
+ errorMessage?: string | null;
153
+ /** What the run was started with; the surface snapshot names its entity. */
154
+ input?: {
155
+ context?: {
156
+ surface?: RobinSurfaceContext;
157
+ };
158
+ } | null;
159
+ messages: RobinPlatformMessage[];
160
+ events: RobinRunEvent[];
161
+ delegations: RobinDelegation[];
162
+ }
163
+ export interface RobinSession {
164
+ id: string;
165
+ title?: string | null;
166
+ lastMessageAt?: string | null;
167
+ /** Pinned by its owner in the chat list (RS-26). */
168
+ pinnedAt?: string | null;
169
+ createdAt: string;
170
+ }
171
+ export interface RobinSessionListResponse {
172
+ items: RobinSession[];
173
+ total: number;
174
+ }
175
+ export interface RobinSessionDetail extends RobinSession {
176
+ messages: RobinPlatformMessage[];
177
+ latestRun: RobinRunDetail | null;
178
+ }
179
+ export interface RobinSendMessageResponse {
180
+ sessionId: string;
181
+ messageId: string;
182
+ runId: string;
183
+ status: RobinRunStatus;
184
+ message: string;
185
+ }
186
+ export interface RobinConfirmationPolicy extends RobinActionEffect {
187
+ confirmationRoles?: string[];
188
+ }
189
+ /**
190
+ * The record a confirmed call changes: the open page's entity when the input
191
+ * names it, else Robin's description; `matchesPage: false` when the call is
192
+ * about another record than the open page (`page`).
193
+ */
194
+ export interface RobinConfirmationRecord {
195
+ label?: string;
196
+ page?: string;
197
+ matchesPage?: boolean;
198
+ }
199
+ export interface RobinConfirmation {
200
+ id: string;
201
+ delegationId: string;
202
+ robinRunId: string;
203
+ capabilityId?: string | null;
204
+ status: 'PENDING' | 'CONFIRMED' | 'REJECTED' | 'CANCELLED';
205
+ riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
206
+ policySnapshot: RobinConfirmationPolicy;
207
+ inputSummary: {
208
+ capabilityKey?: string;
209
+ capabilityName?: string;
210
+ input?: Record<string, unknown>;
211
+ /** Readable label per input key, from the capability's input schema. */
212
+ labels?: Record<string, string>;
213
+ /** Input keys with technical ids the person cannot check; not shown. */
214
+ hidden?: string[];
215
+ /** The record the call changes (RS-25). */
216
+ record?: RobinConfirmationRecord;
217
+ /** What the call changes, in Robin's words with names and values ("Neue Position: 2 × 1014 Test"). */
218
+ change?: string;
219
+ };
220
+ /** Roles that may decide, from the policy; filled by the platform's list of open confirmations. */
221
+ requiredRoles?: string[];
222
+ /** Whether the caller holds one of {@link requiredRoles} (RA-REQ-041). */
223
+ canDecide?: boolean;
224
+ /** Conversation of the run, so a chat list can flag waiting conversations (RA-REQ-042). */
225
+ sessionId?: string | null;
226
+ createdAt: string;
227
+ }
228
+ export interface RobinShortcutStep {
229
+ capabilityKey: string;
230
+ name: string;
231
+ requiresConfirmation: boolean;
232
+ }
233
+ export interface RobinShortcut {
234
+ key: string;
235
+ name: string;
236
+ description: string;
237
+ goal: string;
238
+ steps: RobinShortcutStep[];
239
+ confirmations: number;
240
+ }
241
+ export interface RobinStoredAttachment extends RobinSurfaceAttachment {
242
+ size: number;
243
+ expiresAt: string;
244
+ }
245
+ /** One subagent step of a run as the chat shows it, by name (RS-REQ-030, RA-REQ-024). */
246
+ export interface RobinRunStep {
247
+ key: string;
248
+ label: string;
249
+ state: 'planned' | 'running' | 'waiting_confirmation' | 'done' | 'failed';
250
+ }
251
+ /** Body of `PUT /robin/devices/:installationId` (RA-05). */
252
+ export interface RobinDeviceRegistration {
253
+ /** Expo push token, `ExponentPushToken[…]`. */
254
+ pushToken: string;
255
+ platform: 'ios' | 'android';
256
+ appVersion: string;
257
+ }
258
+ /** Statuses of a run the platform announces as `robin.run.status` (RA-05). */
259
+ export type RobinNotifiedRunStatus = Extract<RobinRunStatus, 'SUCCESS' | 'FAILED' | 'WAITING_FOR_CONFIRMATION'>;
260
+ /** Data a Robin notification carries (RA-06); never the answer or confirmation values. */
261
+ export interface RobinPushData {
262
+ sessionId: string;
263
+ runId: string;
264
+ status: RobinNotifiedRunStatus;
265
+ url: string;
266
+ }
267
+ /** Body of a `426` answer: the app version is below the BFF's minimum (RA-REQ-082). */
268
+ export interface RobinUpgradeRequired {
269
+ statusCode: 426;
270
+ message: string;
271
+ minVersion: string;
272
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Wire types of the Robin BFF (`apps/robin-api`) shared by the Angular library
3
+ * `libs/robin` and the Robin-App `apps/robin-mobile` (docs/features/robin-app,
4
+ * RA-07). They mirror the shared types of the AI platform
5
+ * (`@ai/shared/types`, `agents-robin*.types.ts`); the platform is the source
6
+ * of truth, the BFF passes the shapes through unchanged.
7
+ */
8
+ export const TERMINAL_RUN_STATUSES = ['SUCCESS', 'FAILED', 'CANCELLED'];
9
+ export const isTerminalRunStatus = (status) => TERMINAL_RUN_STATUSES.includes(status);
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../libs/robin-client/src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2JH,MAAM,CAAC,MAAM,qBAAqB,GAA8B,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAEnG,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA+B,EAAW,EAAE,CAC7E,qBAA2C,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC"}