@breign/db-schemas 0.1.56 → 0.1.57
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/types/conversation.d.ts +76 -7
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/types/pending-tool-call.d.ts +82 -0
- package/dist/types/pending-tool-call.js +2 -0
- package/dist/types/tool-approval.d.ts +63 -0
- package/dist/types/tool-approval.js +2 -0
- package/dist/types/tool-result.d.ts +19 -0
- package/dist/types/tool-result.js +2 -0
- package/package.json +1 -1
- package/src/types/conversation.ts +83 -7
- package/src/types/index.ts +3 -0
- package/src/types/pending-tool-call.ts +87 -0
- package/src/types/tool-approval.ts +64 -0
- package/src/types/tool-result.ts +21 -0
|
@@ -1,19 +1,81 @@
|
|
|
1
1
|
import { ChatMessageRoleUio } from '@breign/client';
|
|
2
|
+
import type { ToolCallApprovalScope } from './pending-tool-call';
|
|
3
|
+
import type { ToolAction, ToolResult } from './tool-result';
|
|
2
4
|
export type ConversationMessageReaction = 'positive' | 'negative';
|
|
3
|
-
export type
|
|
5
|
+
export type ConversationAttachmentStatus = 'TO_UPLOAD' | 'PROCESSING' | 'PROCESSED' | 'ERROR';
|
|
6
|
+
export type ConversationAttachmentErrorCode = 'ATTACHMENT_TOO_LARGE';
|
|
7
|
+
/**
|
|
8
|
+
* Pièce jointe documentaire. `processed` dit où son contenu a fini : recopié tel quel dans le
|
|
9
|
+
* stockage, ou indexé dans un vector store — les deux formes n'ont rien en commun, d'où l'union.
|
|
10
|
+
*/
|
|
11
|
+
export type ConversationFileAttachment = {
|
|
12
|
+
type: 'file';
|
|
4
13
|
id: string;
|
|
5
|
-
type: string;
|
|
6
14
|
name: string;
|
|
7
15
|
mimeType: string;
|
|
8
16
|
ext: string;
|
|
9
17
|
size: number;
|
|
10
18
|
key: string;
|
|
11
|
-
status:
|
|
19
|
+
status: ConversationAttachmentStatus;
|
|
20
|
+
errorCode?: ConversationAttachmentErrorCode;
|
|
21
|
+
estimatedTokens?: number;
|
|
22
|
+
maxTokens?: number;
|
|
12
23
|
/** Run the OCR pre-pass before parsing this attachment to Markdown */
|
|
13
24
|
ocr?: boolean;
|
|
25
|
+
processed?: {
|
|
26
|
+
type: 'file';
|
|
27
|
+
key: string;
|
|
28
|
+
content?: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: 'indexed';
|
|
31
|
+
index: string;
|
|
32
|
+
backend: 'weaviate' | 'qdrant';
|
|
33
|
+
fileId: string;
|
|
34
|
+
sourceKey: string;
|
|
35
|
+
indexedAt: string;
|
|
36
|
+
invalidatedAt?: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export type ConversationImageAttachment = {
|
|
40
|
+
type: 'image';
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
mimeType: string;
|
|
44
|
+
ext: string;
|
|
45
|
+
size: number;
|
|
46
|
+
key: string;
|
|
14
47
|
url?: string;
|
|
48
|
+
status: ConversationAttachmentStatus;
|
|
15
49
|
inlineDataUrl?: string;
|
|
16
|
-
|
|
50
|
+
};
|
|
51
|
+
/** Discriminée par `type` : une image ne passe pas par le parsing documentaire. */
|
|
52
|
+
export type ConversationAttachment = ConversationFileAttachment | ConversationImageAttachment;
|
|
53
|
+
/**
|
|
54
|
+
* Une décision d'autorisation prise pendant ce message : quel outil, sur quoi, par qui, quand.
|
|
55
|
+
*
|
|
56
|
+
* Portée par le MESSAGE et pas seulement par l'appel d'outil qui a posé la question, parce que
|
|
57
|
+
* c'est l'historique qui doit la montrer : la conversation se relit telle quelle, sans aller
|
|
58
|
+
* chercher les appels différés un par un. Un accord ponctuel n'existant nulle part ailleurs — il
|
|
59
|
+
* n'écrit aucune ligne d'autorisation permanente — c'est ici sa seule trace durable.
|
|
60
|
+
*
|
|
61
|
+
* Délibérément à côté de `toolResults` et non dedans : `toolResults` est **rejoué au modèle** à
|
|
62
|
+
* chaque tour (`OUTILS: …`). Un accord `once` y figurant ferait croire au modèle, deux tours plus
|
|
63
|
+
* tard, qu'il a encore un droit qui a expiré — et c'est exactement le malentendu que la portée
|
|
64
|
+
* `once` existe pour éviter.
|
|
65
|
+
*/
|
|
66
|
+
export type ConversationMessageApproval = {
|
|
67
|
+
/** L'outil dont un usage a été soumis à autorisation. Dit aussi de quelle famille il s'agit. */
|
|
68
|
+
tool: string;
|
|
69
|
+
/** Les refus sont conservés : une décision de ne pas autoriser se relit autant qu'un accord. */
|
|
70
|
+
granted: boolean;
|
|
71
|
+
/** Ce qui a été accordé — ou, sur un refus, ce qui avait été demandé. Jamais l'URL complète. */
|
|
72
|
+
resource: string;
|
|
73
|
+
/** Absent sur un refus, qui n'accorde rien et n'a donc pas de portée. */
|
|
74
|
+
scope?: ToolCallApprovalScope;
|
|
75
|
+
/** Nom ou e-mail de la personne qui a décidé, figé à cet instant. */
|
|
76
|
+
approvedBy?: string;
|
|
77
|
+
/** Quand la décision a été prise, ISO. */
|
|
78
|
+
at: string;
|
|
17
79
|
};
|
|
18
80
|
export type ConversationMessage = {
|
|
19
81
|
id: string;
|
|
@@ -29,8 +91,10 @@ export type ConversationMessage = {
|
|
|
29
91
|
toolName?: string;
|
|
30
92
|
params?: string;
|
|
31
93
|
};
|
|
32
|
-
result?:
|
|
94
|
+
result?: ToolResult;
|
|
33
95
|
}[];
|
|
96
|
+
/** Décisions d'autorisation prises pendant ce message. Voir ConversationMessageApproval. */
|
|
97
|
+
approvals?: ConversationMessageApproval[];
|
|
34
98
|
suggestions?: {
|
|
35
99
|
id: string;
|
|
36
100
|
text: string;
|
|
@@ -42,9 +106,14 @@ export type ConversationMessage = {
|
|
|
42
106
|
flowNodeRequirementsCheckReason?: string;
|
|
43
107
|
flowNodeIterations?: number;
|
|
44
108
|
timestamp: string;
|
|
45
|
-
actions?:
|
|
109
|
+
actions?: ToolAction[];
|
|
46
110
|
attachments?: ConversationAttachment[];
|
|
47
|
-
|
|
111
|
+
/**
|
|
112
|
+
* `waiting_client` : le tour s'est arrêté sur un outil que le client doit exécuter ou autoriser.
|
|
113
|
+
* Ni terminé ni en erreur — le message repartira en `generating` quand le résultat sera remonté.
|
|
114
|
+
* Les appels en attente eux-mêmes se lisent par message (voir `PendingToolCall`).
|
|
115
|
+
*/
|
|
116
|
+
status?: 'generating' | 'completed' | 'error' | 'tool_calling' | 'moderated' | 'waiting_client';
|
|
48
117
|
toolCall?: {
|
|
49
118
|
name: string;
|
|
50
119
|
stepIndex: number;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './flow';
|
|
|
10
10
|
export * from './flow-editor';
|
|
11
11
|
export * from './knowledge';
|
|
12
12
|
export * from './organization';
|
|
13
|
+
export * from './pending-tool-call';
|
|
13
14
|
export * from './permissions';
|
|
14
15
|
export * from './role';
|
|
15
16
|
export * from './scheduled-task';
|
|
@@ -17,5 +18,7 @@ export * from './scheduled-task-run';
|
|
|
17
18
|
export * from './skill';
|
|
18
19
|
export * from './speech-to-text';
|
|
19
20
|
export * from './tool';
|
|
21
|
+
export * from './tool-approval';
|
|
22
|
+
export * from './tool-result';
|
|
20
23
|
export * from './user';
|
|
21
24
|
export * from './workspace-file';
|
package/dist/types/index.js
CHANGED
|
@@ -26,6 +26,7 @@ __exportStar(require("./flow"), exports);
|
|
|
26
26
|
__exportStar(require("./flow-editor"), exports);
|
|
27
27
|
__exportStar(require("./knowledge"), exports);
|
|
28
28
|
__exportStar(require("./organization"), exports);
|
|
29
|
+
__exportStar(require("./pending-tool-call"), exports);
|
|
29
30
|
__exportStar(require("./permissions"), exports);
|
|
30
31
|
__exportStar(require("./role"), exports);
|
|
31
32
|
__exportStar(require("./scheduled-task"), exports);
|
|
@@ -33,5 +34,7 @@ __exportStar(require("./scheduled-task-run"), exports);
|
|
|
33
34
|
__exportStar(require("./skill"), exports);
|
|
34
35
|
__exportStar(require("./speech-to-text"), exports);
|
|
35
36
|
__exportStar(require("./tool"), exports);
|
|
37
|
+
__exportStar(require("./tool-approval"), exports);
|
|
38
|
+
__exportStar(require("./tool-result"), exports);
|
|
36
39
|
__exportStar(require("./user"), exports);
|
|
37
40
|
__exportStar(require("./workspace-file"), exports);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ToolResult } from './tool-result';
|
|
2
|
+
export type PendingToolCallStatus = 'pending' | 'completed' | 'failed' | 'refused' | 'expired';
|
|
3
|
+
/** Les statuts qu'un client peut poser : `pending` et `expired` sont au serveur seul. */
|
|
4
|
+
export type PendingToolCallResolution = Exclude<PendingToolCallStatus, 'pending' | 'expired'>;
|
|
5
|
+
/**
|
|
6
|
+
* Jusqu'où porte un « oui » donné sur une demande d'autorisation.
|
|
7
|
+
*
|
|
8
|
+
* `once` = la réponse suspendue, et rien après : **aucune ligne n'est écrite dans `ToolApproval`**,
|
|
9
|
+
* et la même demande est reposée au message suivant. `agentUser` = cet agent, pour cet utilisateur,
|
|
10
|
+
* dans toutes ses conversations, jusqu'à révocation — là une ligne existe.
|
|
11
|
+
*
|
|
12
|
+
* Volontairement distinct de `ToolApprovalScopeKind` : celui-ci nomme des portées de STOCKAGE, et
|
|
13
|
+
* `once` n'en est pas une. Les confondre reviendrait à admettre qu'un accord ponctuel puisse être
|
|
14
|
+
* stocké, ce qui est exactement ce qu'il ne doit pas être.
|
|
15
|
+
*/
|
|
16
|
+
export type ToolCallApprovalScope = 'once' | 'agentUser';
|
|
17
|
+
/**
|
|
18
|
+
* Ce qu'un « oui » a accordé, porté par l'appel d'outil qui a posé la question.
|
|
19
|
+
*
|
|
20
|
+
* Deux rôles, et c'est voulu. C'est la TRACE d'une décision — qui, quoi, jusqu'où — que l'écran de
|
|
21
|
+
* conversation réaffiche, et qui reste lisible que le droit permanent ait été créé ou non : avec
|
|
22
|
+
* `once` il n'y en a jamais eu, avec `agentUser` il a pu être révoqué depuis. Et pour la portée
|
|
23
|
+
* `once` c'est **le droit lui-même**, qui n'existe nulle part ailleurs : le tour de reprise le relit
|
|
24
|
+
* ici. D'où la borne — un appel appartient à un message assistant, donc un accord ponctuel ne peut
|
|
25
|
+
* pas survivre à ce message.
|
|
26
|
+
*
|
|
27
|
+
* C'est ici et pas dans `ToolApproval` parce que la question posée est propre à UNE conversation,
|
|
28
|
+
* alors que `ToolApproval` décrit un droit permanent attaché à un agent et à un utilisateur.
|
|
29
|
+
*/
|
|
30
|
+
export type ToolCallApproval = {
|
|
31
|
+
scope: ToolCallApprovalScope;
|
|
32
|
+
/**
|
|
33
|
+
* Ce qui est accordé, préfixé par sa famille exactement comme `ToolApproval.resourceKey` — `url:`
|
|
34
|
+
* une adresse, `cmd:` une commande exacte, `glob:` un motif. Un seul analyseur pour les deux, et
|
|
35
|
+
* une adresse approuvée ne peut jamais satisfaire une commande.
|
|
36
|
+
*/
|
|
37
|
+
resourceKey: string;
|
|
38
|
+
/**
|
|
39
|
+
* Nom ou e-mail de la personne qui a cliqué, figé à cet instant plutôt que joint plus tard : la
|
|
40
|
+
* trace doit rester lisible après son départ de l'organisation. Absent quand l'appelant n'a pas
|
|
41
|
+
* d'identité.
|
|
42
|
+
*/
|
|
43
|
+
approvedBy?: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Un appel d'outil que le serveur ne résout pas lui-même. Le tour est clos dès sa création et la
|
|
47
|
+
* réponse du client relance un tour de reprise : un appel non résolu ne retient rien. Générique —
|
|
48
|
+
* `tool` le nomme, `arguments` porte ce que le modèle a passé.
|
|
49
|
+
*
|
|
50
|
+
* Le lien avec la conversation va d'ici vers le message (`chatId` + `messageId`), jamais l'inverse :
|
|
51
|
+
* le message ne porte que le statut `waiting_client`, et les appels se lisent par message.
|
|
52
|
+
*/
|
|
53
|
+
export type PendingToolCall = {
|
|
54
|
+
id: string;
|
|
55
|
+
tool: string;
|
|
56
|
+
userId: string;
|
|
57
|
+
chatId?: string;
|
|
58
|
+
/** Message assistant suspendu : celui que le tour de reprise complétera. */
|
|
59
|
+
messageId?: string;
|
|
60
|
+
/** Device visé, à titre informatif : l'autorisation passe par la conversation. */
|
|
61
|
+
deviceId?: string;
|
|
62
|
+
status: PendingToolCallStatus;
|
|
63
|
+
arguments?: Record<string, unknown>;
|
|
64
|
+
/**
|
|
65
|
+
* Ce que la reprise écrit dans la conversation à la place des `arguments` bruts. Le tour de reprise
|
|
66
|
+
* injecte `Outil : <tool>(<arguments>)` dans l'historique **persisté**, rejoué à chaque tour : un
|
|
67
|
+
* argument portant un secret y resterait indéfiniment. Absent ⇒ repli sur les `arguments`.
|
|
68
|
+
*/
|
|
69
|
+
displaySummary?: string;
|
|
70
|
+
result?: ToolResult;
|
|
71
|
+
/**
|
|
72
|
+
* Écrit à la résolution, sur les seuls appels d'approbation et seulement quand la réponse est un
|
|
73
|
+
* accord. Posé dans la même écriture que le statut : pour une portée `once` il EST le droit, donc
|
|
74
|
+
* un appel passé à `completed` sans lui relancerait le tour sans rien accorder.
|
|
75
|
+
*/
|
|
76
|
+
approval?: ToolCallApproval;
|
|
77
|
+
error?: string;
|
|
78
|
+
createdAt: Date;
|
|
79
|
+
updatedAt: Date;
|
|
80
|
+
/** Échéance optionnelle, posée outil par outil : un outil sans TTL n'expire jamais. */
|
|
81
|
+
expiresAt?: Date;
|
|
82
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User approval of one operation the agent's sandbox asked permission for.
|
|
3
|
+
*
|
|
4
|
+
* Two families exist, both belonging to `Bash_exec`: an address its curl may reach, and a python
|
|
5
|
+
* command it may run. When such a call is not already granted, the conversation turn is suspended
|
|
6
|
+
* and the user is asked. A "yes" is stored here ONLY when the user chose to stop being asked: a
|
|
7
|
+
* one-off "allow this once" grants the suspended turn and writes nothing, so every row in this
|
|
8
|
+
* table is a standing right, not a trace of a past decision. That trace lives on the tool call —
|
|
9
|
+
* see `ToolCallApproval`, which is where a one-off is recorded and where it stays confined.
|
|
10
|
+
*
|
|
11
|
+
* Three invariants make this table safe to read on the hot path:
|
|
12
|
+
*
|
|
13
|
+
* - **It can only intersect, never widen.** An approval unlocks an operation of a tool the agent
|
|
14
|
+
* already has, behind the server lock and the per-agent switch. An agent with no sandbox gains
|
|
15
|
+
* nothing because the user approved something elsewhere.
|
|
16
|
+
* - **`resourceKey` is normalised, and carries its family as a prefix.** A URL prefix, a glob or an
|
|
17
|
+
* exact command line — never raw arguments, so a secret passed next to the approved value is not
|
|
18
|
+
* persisted here. The prefix is what stops an approved address from authorising a command: the
|
|
19
|
+
* full arguments live only in the tool call that produced the approval.
|
|
20
|
+
* - **A row is a personal consent, and `userId` says whose.** Nobody can approve on someone else's
|
|
21
|
+
* behalf: the resolution route refuses a caller who is not the person the question was asked of,
|
|
22
|
+
* so the person the right applies to and the person who granted it are the same by construction.
|
|
23
|
+
* One column carries both, and it is what the management routes key on.
|
|
24
|
+
*
|
|
25
|
+
* Revoking one deletes its row: there is no state a revoked approval could be in, so nothing on the
|
|
26
|
+
* read path has to filter it out.
|
|
27
|
+
*
|
|
28
|
+
* Shared with agent-tasks through this package even though agent-tasks asks nobody: the type
|
|
29
|
+
* belongs with the other storage models.
|
|
30
|
+
*/
|
|
31
|
+
export type ToolApprovalScopeKind = 'agentUser' | 'user';
|
|
32
|
+
export type ToolApproval = {
|
|
33
|
+
id: string;
|
|
34
|
+
/**
|
|
35
|
+
* British spelling "organisationId" — aligned with Agent.organisationId (domain + Mongo
|
|
36
|
+
* storage). In PostgreSQL the COLUMN is organizationId ("z"), mapped inside the PG adapter.
|
|
37
|
+
* It leads every key: an approval never crosses an organisation boundary.
|
|
38
|
+
*/
|
|
39
|
+
organisationId: string;
|
|
40
|
+
/**
|
|
41
|
+
* `agentUser` is the only one written. `user` is kept readable but nothing writes it — see
|
|
42
|
+
* `agentApprovalScope` in brain-api, the single place that builds a stored scope.
|
|
43
|
+
*/
|
|
44
|
+
scopeKind: ToolApprovalScopeKind;
|
|
45
|
+
/**
|
|
46
|
+
* Scope folded into a single string, so unicity is a plain unique index. PostgreSQL treats two
|
|
47
|
+
* NULLs as distinct, so a unique index over nullable discriminator columns would not constrain
|
|
48
|
+
* anything — same reason `workspaceSessionKey` exists.
|
|
49
|
+
*/
|
|
50
|
+
scopeKey: string;
|
|
51
|
+
/** Set for the 'agentUser' scope. Absent on 'user', which holds for every agent. */
|
|
52
|
+
agentId?: string;
|
|
53
|
+
/** Whose consent it is. Never empty: an approval always belongs to one person. */
|
|
54
|
+
userId: string;
|
|
55
|
+
/** The tool as the model calls it. Part of the key: two tools never share an approval. */
|
|
56
|
+
toolName: string;
|
|
57
|
+
/**
|
|
58
|
+
* What a "yes" grants, prefixed by its family: `url:` an origin plus an optional first path
|
|
59
|
+
* segment, `glob:` a pattern covering a site, `cmd:` one exact command line.
|
|
60
|
+
*/
|
|
61
|
+
resourceKey: string;
|
|
62
|
+
createdAt: string;
|
|
63
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ConversationAttachment } from './conversation';
|
|
2
|
+
/**
|
|
3
|
+
* Ce qu'un outil renvoie au tour de conversation. Copie structurelle de `ToolResult` /
|
|
4
|
+
* `ToolAction` de brain-api (`src/shared/init-tools.ts`), qui reste la définition de référence :
|
|
5
|
+
* ce paquet ne dépend pas de brain-api, mais les modèles de stockage qui portent un résultat
|
|
6
|
+
* d'outil — un message de conversation, un appel d'outil en attente — ont besoin de sa forme.
|
|
7
|
+
*/
|
|
8
|
+
export type ToolAction = {
|
|
9
|
+
code: string;
|
|
10
|
+
} & Record<string, unknown>;
|
|
11
|
+
export type ToolResult = {
|
|
12
|
+
text: string;
|
|
13
|
+
attachments?: ConversationAttachment[];
|
|
14
|
+
askConfirmation?: boolean;
|
|
15
|
+
confirmationLink?: string;
|
|
16
|
+
actions?: ToolAction[];
|
|
17
|
+
/** Clôt le tour sans réponse finale : le client doit résoudre l'appel avant qu'il reprenne. */
|
|
18
|
+
suspendTurn?: boolean;
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,21 +1,90 @@
|
|
|
1
1
|
import { ChatMessageRoleUio } from '@breign/client';
|
|
2
|
+
import type { ToolCallApprovalScope } from './pending-tool-call';
|
|
3
|
+
import type { ToolAction, ToolResult } from './tool-result';
|
|
2
4
|
|
|
3
5
|
export type ConversationMessageReaction = 'positive' | 'negative';
|
|
4
6
|
|
|
5
|
-
export type
|
|
7
|
+
export type ConversationAttachmentStatus = 'TO_UPLOAD' | 'PROCESSING' | 'PROCESSED' | 'ERROR';
|
|
8
|
+
|
|
9
|
+
export type ConversationAttachmentErrorCode = 'ATTACHMENT_TOO_LARGE';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Pièce jointe documentaire. `processed` dit où son contenu a fini : recopié tel quel dans le
|
|
13
|
+
* stockage, ou indexé dans un vector store — les deux formes n'ont rien en commun, d'où l'union.
|
|
14
|
+
*/
|
|
15
|
+
export type ConversationFileAttachment = {
|
|
16
|
+
type: 'file';
|
|
6
17
|
id: string;
|
|
7
|
-
type: string;
|
|
8
18
|
name: string;
|
|
9
19
|
mimeType: string;
|
|
10
20
|
ext: string;
|
|
11
21
|
size: number;
|
|
12
22
|
key: string;
|
|
13
|
-
status:
|
|
23
|
+
status: ConversationAttachmentStatus;
|
|
24
|
+
errorCode?: ConversationAttachmentErrorCode;
|
|
25
|
+
estimatedTokens?: number;
|
|
26
|
+
maxTokens?: number;
|
|
14
27
|
/** Run the OCR pre-pass before parsing this attachment to Markdown */
|
|
15
28
|
ocr?: boolean;
|
|
29
|
+
processed?:
|
|
30
|
+
| {
|
|
31
|
+
type: 'file';
|
|
32
|
+
key: string;
|
|
33
|
+
content?: string;
|
|
34
|
+
}
|
|
35
|
+
| {
|
|
36
|
+
type: 'indexed';
|
|
37
|
+
index: string;
|
|
38
|
+
backend: 'weaviate' | 'qdrant';
|
|
39
|
+
fileId: string;
|
|
40
|
+
sourceKey: string;
|
|
41
|
+
indexedAt: string;
|
|
42
|
+
invalidatedAt?: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type ConversationImageAttachment = {
|
|
47
|
+
type: 'image';
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
mimeType: string;
|
|
51
|
+
ext: string;
|
|
52
|
+
size: number;
|
|
53
|
+
key: string;
|
|
16
54
|
url?: string;
|
|
55
|
+
status: ConversationAttachmentStatus;
|
|
17
56
|
inlineDataUrl?: string;
|
|
18
|
-
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Discriminée par `type` : une image ne passe pas par le parsing documentaire. */
|
|
60
|
+
export type ConversationAttachment = ConversationFileAttachment | ConversationImageAttachment;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Une décision d'autorisation prise pendant ce message : quel outil, sur quoi, par qui, quand.
|
|
64
|
+
*
|
|
65
|
+
* Portée par le MESSAGE et pas seulement par l'appel d'outil qui a posé la question, parce que
|
|
66
|
+
* c'est l'historique qui doit la montrer : la conversation se relit telle quelle, sans aller
|
|
67
|
+
* chercher les appels différés un par un. Un accord ponctuel n'existant nulle part ailleurs — il
|
|
68
|
+
* n'écrit aucune ligne d'autorisation permanente — c'est ici sa seule trace durable.
|
|
69
|
+
*
|
|
70
|
+
* Délibérément à côté de `toolResults` et non dedans : `toolResults` est **rejoué au modèle** à
|
|
71
|
+
* chaque tour (`OUTILS: …`). Un accord `once` y figurant ferait croire au modèle, deux tours plus
|
|
72
|
+
* tard, qu'il a encore un droit qui a expiré — et c'est exactement le malentendu que la portée
|
|
73
|
+
* `once` existe pour éviter.
|
|
74
|
+
*/
|
|
75
|
+
export type ConversationMessageApproval = {
|
|
76
|
+
/** L'outil dont un usage a été soumis à autorisation. Dit aussi de quelle famille il s'agit. */
|
|
77
|
+
tool: string;
|
|
78
|
+
/** Les refus sont conservés : une décision de ne pas autoriser se relit autant qu'un accord. */
|
|
79
|
+
granted: boolean;
|
|
80
|
+
/** Ce qui a été accordé — ou, sur un refus, ce qui avait été demandé. Jamais l'URL complète. */
|
|
81
|
+
resource: string;
|
|
82
|
+
/** Absent sur un refus, qui n'accorde rien et n'a donc pas de portée. */
|
|
83
|
+
scope?: ToolCallApprovalScope;
|
|
84
|
+
/** Nom ou e-mail de la personne qui a décidé, figé à cet instant. */
|
|
85
|
+
approvedBy?: string;
|
|
86
|
+
/** Quand la décision a été prise, ISO. */
|
|
87
|
+
at: string;
|
|
19
88
|
};
|
|
20
89
|
|
|
21
90
|
export type ConversationMessage = {
|
|
@@ -28,8 +97,10 @@ export type ConversationMessage = {
|
|
|
28
97
|
confirmationLink?: string;
|
|
29
98
|
toolResults?: {
|
|
30
99
|
tool: { tool: string; toolName?: string; params?: string };
|
|
31
|
-
result?:
|
|
100
|
+
result?: ToolResult;
|
|
32
101
|
}[];
|
|
102
|
+
/** Décisions d'autorisation prises pendant ce message. Voir ConversationMessageApproval. */
|
|
103
|
+
approvals?: ConversationMessageApproval[];
|
|
33
104
|
suggestions?: {
|
|
34
105
|
id: string;
|
|
35
106
|
text: string;
|
|
@@ -41,9 +112,14 @@ export type ConversationMessage = {
|
|
|
41
112
|
flowNodeRequirementsCheckReason?: string;
|
|
42
113
|
flowNodeIterations?: number;
|
|
43
114
|
timestamp: string;
|
|
44
|
-
actions?:
|
|
115
|
+
actions?: ToolAction[];
|
|
45
116
|
attachments?: ConversationAttachment[];
|
|
46
|
-
|
|
117
|
+
/**
|
|
118
|
+
* `waiting_client` : le tour s'est arrêté sur un outil que le client doit exécuter ou autoriser.
|
|
119
|
+
* Ni terminé ni en erreur — le message repartira en `generating` quand le résultat sera remonté.
|
|
120
|
+
* Les appels en attente eux-mêmes se lisent par message (voir `PendingToolCall`).
|
|
121
|
+
*/
|
|
122
|
+
status?: 'generating' | 'completed' | 'error' | 'tool_calling' | 'moderated' | 'waiting_client';
|
|
47
123
|
toolCall?: { name: string; stepIndex: number };
|
|
48
124
|
error?: {
|
|
49
125
|
code: string;
|
package/src/types/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './flow';
|
|
|
10
10
|
export * from './flow-editor';
|
|
11
11
|
export * from './knowledge';
|
|
12
12
|
export * from './organization';
|
|
13
|
+
export * from './pending-tool-call';
|
|
13
14
|
export * from './permissions';
|
|
14
15
|
export * from './role';
|
|
15
16
|
export * from './scheduled-task';
|
|
@@ -17,5 +18,7 @@ export * from './scheduled-task-run';
|
|
|
17
18
|
export * from './skill';
|
|
18
19
|
export * from './speech-to-text';
|
|
19
20
|
export * from './tool';
|
|
21
|
+
export * from './tool-approval';
|
|
22
|
+
export * from './tool-result';
|
|
20
23
|
export * from './user';
|
|
21
24
|
export * from './workspace-file';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ToolResult } from './tool-result';
|
|
2
|
+
|
|
3
|
+
export type PendingToolCallStatus = 'pending' | 'completed' | 'failed' | 'refused' | 'expired';
|
|
4
|
+
|
|
5
|
+
/** Les statuts qu'un client peut poser : `pending` et `expired` sont au serveur seul. */
|
|
6
|
+
export type PendingToolCallResolution = Exclude<PendingToolCallStatus, 'pending' | 'expired'>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Jusqu'où porte un « oui » donné sur une demande d'autorisation.
|
|
10
|
+
*
|
|
11
|
+
* `once` = la réponse suspendue, et rien après : **aucune ligne n'est écrite dans `ToolApproval`**,
|
|
12
|
+
* et la même demande est reposée au message suivant. `agentUser` = cet agent, pour cet utilisateur,
|
|
13
|
+
* dans toutes ses conversations, jusqu'à révocation — là une ligne existe.
|
|
14
|
+
*
|
|
15
|
+
* Volontairement distinct de `ToolApprovalScopeKind` : celui-ci nomme des portées de STOCKAGE, et
|
|
16
|
+
* `once` n'en est pas une. Les confondre reviendrait à admettre qu'un accord ponctuel puisse être
|
|
17
|
+
* stocké, ce qui est exactement ce qu'il ne doit pas être.
|
|
18
|
+
*/
|
|
19
|
+
export type ToolCallApprovalScope = 'once' | 'agentUser';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ce qu'un « oui » a accordé, porté par l'appel d'outil qui a posé la question.
|
|
23
|
+
*
|
|
24
|
+
* Deux rôles, et c'est voulu. C'est la TRACE d'une décision — qui, quoi, jusqu'où — que l'écran de
|
|
25
|
+
* conversation réaffiche, et qui reste lisible que le droit permanent ait été créé ou non : avec
|
|
26
|
+
* `once` il n'y en a jamais eu, avec `agentUser` il a pu être révoqué depuis. Et pour la portée
|
|
27
|
+
* `once` c'est **le droit lui-même**, qui n'existe nulle part ailleurs : le tour de reprise le relit
|
|
28
|
+
* ici. D'où la borne — un appel appartient à un message assistant, donc un accord ponctuel ne peut
|
|
29
|
+
* pas survivre à ce message.
|
|
30
|
+
*
|
|
31
|
+
* C'est ici et pas dans `ToolApproval` parce que la question posée est propre à UNE conversation,
|
|
32
|
+
* alors que `ToolApproval` décrit un droit permanent attaché à un agent et à un utilisateur.
|
|
33
|
+
*/
|
|
34
|
+
export type ToolCallApproval = {
|
|
35
|
+
scope: ToolCallApprovalScope;
|
|
36
|
+
/**
|
|
37
|
+
* Ce qui est accordé, préfixé par sa famille exactement comme `ToolApproval.resourceKey` — `url:`
|
|
38
|
+
* une adresse, `cmd:` une commande exacte, `glob:` un motif. Un seul analyseur pour les deux, et
|
|
39
|
+
* une adresse approuvée ne peut jamais satisfaire une commande.
|
|
40
|
+
*/
|
|
41
|
+
resourceKey: string;
|
|
42
|
+
/**
|
|
43
|
+
* Nom ou e-mail de la personne qui a cliqué, figé à cet instant plutôt que joint plus tard : la
|
|
44
|
+
* trace doit rester lisible après son départ de l'organisation. Absent quand l'appelant n'a pas
|
|
45
|
+
* d'identité.
|
|
46
|
+
*/
|
|
47
|
+
approvedBy?: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Un appel d'outil que le serveur ne résout pas lui-même. Le tour est clos dès sa création et la
|
|
52
|
+
* réponse du client relance un tour de reprise : un appel non résolu ne retient rien. Générique —
|
|
53
|
+
* `tool` le nomme, `arguments` porte ce que le modèle a passé.
|
|
54
|
+
*
|
|
55
|
+
* Le lien avec la conversation va d'ici vers le message (`chatId` + `messageId`), jamais l'inverse :
|
|
56
|
+
* le message ne porte que le statut `waiting_client`, et les appels se lisent par message.
|
|
57
|
+
*/
|
|
58
|
+
export type PendingToolCall = {
|
|
59
|
+
id: string;
|
|
60
|
+
tool: string;
|
|
61
|
+
userId: string;
|
|
62
|
+
chatId?: string;
|
|
63
|
+
/** Message assistant suspendu : celui que le tour de reprise complétera. */
|
|
64
|
+
messageId?: string;
|
|
65
|
+
/** Device visé, à titre informatif : l'autorisation passe par la conversation. */
|
|
66
|
+
deviceId?: string;
|
|
67
|
+
status: PendingToolCallStatus;
|
|
68
|
+
arguments?: Record<string, unknown>;
|
|
69
|
+
/**
|
|
70
|
+
* Ce que la reprise écrit dans la conversation à la place des `arguments` bruts. Le tour de reprise
|
|
71
|
+
* injecte `Outil : <tool>(<arguments>)` dans l'historique **persisté**, rejoué à chaque tour : un
|
|
72
|
+
* argument portant un secret y resterait indéfiniment. Absent ⇒ repli sur les `arguments`.
|
|
73
|
+
*/
|
|
74
|
+
displaySummary?: string;
|
|
75
|
+
result?: ToolResult;
|
|
76
|
+
/**
|
|
77
|
+
* Écrit à la résolution, sur les seuls appels d'approbation et seulement quand la réponse est un
|
|
78
|
+
* accord. Posé dans la même écriture que le statut : pour une portée `once` il EST le droit, donc
|
|
79
|
+
* un appel passé à `completed` sans lui relancerait le tour sans rien accorder.
|
|
80
|
+
*/
|
|
81
|
+
approval?: ToolCallApproval;
|
|
82
|
+
error?: string;
|
|
83
|
+
createdAt: Date;
|
|
84
|
+
updatedAt: Date;
|
|
85
|
+
/** Échéance optionnelle, posée outil par outil : un outil sans TTL n'expire jamais. */
|
|
86
|
+
expiresAt?: Date;
|
|
87
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User approval of one operation the agent's sandbox asked permission for.
|
|
3
|
+
*
|
|
4
|
+
* Two families exist, both belonging to `Bash_exec`: an address its curl may reach, and a python
|
|
5
|
+
* command it may run. When such a call is not already granted, the conversation turn is suspended
|
|
6
|
+
* and the user is asked. A "yes" is stored here ONLY when the user chose to stop being asked: a
|
|
7
|
+
* one-off "allow this once" grants the suspended turn and writes nothing, so every row in this
|
|
8
|
+
* table is a standing right, not a trace of a past decision. That trace lives on the tool call —
|
|
9
|
+
* see `ToolCallApproval`, which is where a one-off is recorded and where it stays confined.
|
|
10
|
+
*
|
|
11
|
+
* Three invariants make this table safe to read on the hot path:
|
|
12
|
+
*
|
|
13
|
+
* - **It can only intersect, never widen.** An approval unlocks an operation of a tool the agent
|
|
14
|
+
* already has, behind the server lock and the per-agent switch. An agent with no sandbox gains
|
|
15
|
+
* nothing because the user approved something elsewhere.
|
|
16
|
+
* - **`resourceKey` is normalised, and carries its family as a prefix.** A URL prefix, a glob or an
|
|
17
|
+
* exact command line — never raw arguments, so a secret passed next to the approved value is not
|
|
18
|
+
* persisted here. The prefix is what stops an approved address from authorising a command: the
|
|
19
|
+
* full arguments live only in the tool call that produced the approval.
|
|
20
|
+
* - **A row is a personal consent, and `userId` says whose.** Nobody can approve on someone else's
|
|
21
|
+
* behalf: the resolution route refuses a caller who is not the person the question was asked of,
|
|
22
|
+
* so the person the right applies to and the person who granted it are the same by construction.
|
|
23
|
+
* One column carries both, and it is what the management routes key on.
|
|
24
|
+
*
|
|
25
|
+
* Revoking one deletes its row: there is no state a revoked approval could be in, so nothing on the
|
|
26
|
+
* read path has to filter it out.
|
|
27
|
+
*
|
|
28
|
+
* Shared with agent-tasks through this package even though agent-tasks asks nobody: the type
|
|
29
|
+
* belongs with the other storage models.
|
|
30
|
+
*/
|
|
31
|
+
export type ToolApprovalScopeKind = 'agentUser' | 'user';
|
|
32
|
+
|
|
33
|
+
export type ToolApproval = {
|
|
34
|
+
id: string;
|
|
35
|
+
/**
|
|
36
|
+
* British spelling "organisationId" — aligned with Agent.organisationId (domain + Mongo
|
|
37
|
+
* storage). In PostgreSQL the COLUMN is organizationId ("z"), mapped inside the PG adapter.
|
|
38
|
+
* It leads every key: an approval never crosses an organisation boundary.
|
|
39
|
+
*/
|
|
40
|
+
organisationId: string;
|
|
41
|
+
/**
|
|
42
|
+
* `agentUser` is the only one written. `user` is kept readable but nothing writes it — see
|
|
43
|
+
* `agentApprovalScope` in brain-api, the single place that builds a stored scope.
|
|
44
|
+
*/
|
|
45
|
+
scopeKind: ToolApprovalScopeKind;
|
|
46
|
+
/**
|
|
47
|
+
* Scope folded into a single string, so unicity is a plain unique index. PostgreSQL treats two
|
|
48
|
+
* NULLs as distinct, so a unique index over nullable discriminator columns would not constrain
|
|
49
|
+
* anything — same reason `workspaceSessionKey` exists.
|
|
50
|
+
*/
|
|
51
|
+
scopeKey: string;
|
|
52
|
+
/** Set for the 'agentUser' scope. Absent on 'user', which holds for every agent. */
|
|
53
|
+
agentId?: string;
|
|
54
|
+
/** Whose consent it is. Never empty: an approval always belongs to one person. */
|
|
55
|
+
userId: string;
|
|
56
|
+
/** The tool as the model calls it. Part of the key: two tools never share an approval. */
|
|
57
|
+
toolName: string;
|
|
58
|
+
/**
|
|
59
|
+
* What a "yes" grants, prefixed by its family: `url:` an origin plus an optional first path
|
|
60
|
+
* segment, `glob:` a pattern covering a site, `cmd:` one exact command line.
|
|
61
|
+
*/
|
|
62
|
+
resourceKey: string;
|
|
63
|
+
createdAt: string;
|
|
64
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ConversationAttachment } from './conversation';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ce qu'un outil renvoie au tour de conversation. Copie structurelle de `ToolResult` /
|
|
5
|
+
* `ToolAction` de brain-api (`src/shared/init-tools.ts`), qui reste la définition de référence :
|
|
6
|
+
* ce paquet ne dépend pas de brain-api, mais les modèles de stockage qui portent un résultat
|
|
7
|
+
* d'outil — un message de conversation, un appel d'outil en attente — ont besoin de sa forme.
|
|
8
|
+
*/
|
|
9
|
+
export type ToolAction = {
|
|
10
|
+
code: string;
|
|
11
|
+
} & Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
export type ToolResult = {
|
|
14
|
+
text: string;
|
|
15
|
+
attachments?: ConversationAttachment[];
|
|
16
|
+
askConfirmation?: boolean;
|
|
17
|
+
confirmationLink?: string;
|
|
18
|
+
actions?: ToolAction[];
|
|
19
|
+
/** Clôt le tour sans réponse finale : le client doit résoudre l'appel avant qu'il reprenne. */
|
|
20
|
+
suspendTurn?: boolean;
|
|
21
|
+
};
|