mitra-interactions-sdk 1.0.60-beta.48 → 1.0.60-beta.49
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 +1 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +174 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +52 -52
package/README.md
CHANGED
|
@@ -263,7 +263,7 @@ const result2 = await listRecordsMitra({
|
|
|
263
263
|
|
|
264
264
|
## Agent Chat (copilot)
|
|
265
265
|
|
|
266
|
-
Chat com o agente de IA embarcado. **REST-first, tudo pela BFF no `baseURL` normal** (`/agentAiShortcut/*` — herda `fetchWithRefresh` e CORS): chats (`createChat`/`listChats`/`readChat`/`renameChat`/`deleteChat`), histórico (`listChatMessages`), credenciais e modelos. `projectId` obrigatório em todas (opcional na chamada se veio do `configureSdkMitra`). **Só o prompt é streaming** — um WebSocket por task em `wss://{origin}/copilot/ws/tasks/{taskId}?token=JWT`, aberto pela SDK quando a conversa começa (WS não passa por preflight de CORS).
|
|
266
|
+
Chat com o agente de IA embarcado. **REST-first, tudo pela BFF no `baseURL` normal** (`/agentAiShortcut/*` — herda `fetchWithRefresh` e CORS): chats (`createChat`/`listChats`/`readChat`/`renameChat`/`deleteChat`), histórico (`listChatMessages`), credenciais e modelos. `projectId` obrigatório em todas (opcional na chamada se veio do `configureSdkMitra`). **Só o prompt é streaming** — um WebSocket por task em `wss://{origin}/copilot/ws/tasks/{taskId}?token=JWT`, aberto pela SDK quando a conversa começa (WS não passa por preflight de CORS). Chats que nascem no **box T3** têm canal direto. A decisão é do copilot por app e vem no campo `runtime` da task (`RUNNER` ou `T3`), que a SDK lê no `createChat`/`readChat`: **só quando ele é `T3`** a SDK chama `POST /copilot/api/v1/tasks/{id}/channel` antes de conectar e abre o WebSocket na URL devolvida, que aponta ao box do agente pelo gateway com `grant` e `ticket` curtos na query. O envelope de eventos é o mesmo; só a URL muda. Esse POST responde na hora: `200` com `status: "ready"` e a URL, ou `202` com `status: "booting"` enquanto o box ainda sobe — nesse caso a SDK repete a cada 2s até 120s, teto que cobre o boot frio de box T3, que leva até ~2 min. Chat do runner, copilot antigo (que não devolve `runtime`), box que não subiu dentro do teto ou falha nessa chamada seguem pelo WebSocket do copilot, byte a byte como hoje. Cada reconexão pede um canal novo.
|
|
267
267
|
|
|
268
268
|
Precisa de `token` e `baseURL` configurados (o login já deixa pronto). O transporte do prompt aceita `transport: 'ws' | 'http'` — `'ws'` é o default; `'http'` é **funcional**: `POST /copilot/api/v1/tasks/{id}/inputs` + SSE em `/events` via fetch, com a MESMA paridade de eventos do WS (pra ambientes onde WebSocket não rola, ex.: serverless).
|
|
269
269
|
|
package/dist/index.d.mts
CHANGED
|
@@ -651,8 +651,8 @@ declare function refreshTokenSilently(authUrl: string, _projectId: number | stri
|
|
|
651
651
|
* Mitra Interactions SDK — Agent Chat (via BFF)
|
|
652
652
|
*
|
|
653
653
|
* REST-first, tudo pela BFF no baseURL normal (`/agentAiShortcut/*`) — herda
|
|
654
|
-
* fetchWithRefresh e
|
|
655
|
-
* desembrulhadas do Page (result é o array direto).
|
|
654
|
+
* fetchWithRefresh e a troca de token server-side. Envelope `{ status, result }`;
|
|
655
|
+
* listas vêm desembrulhadas do Page (result é o array direto).
|
|
656
656
|
*
|
|
657
657
|
* SÓ o prompt é streaming: WebSocket por task em
|
|
658
658
|
* `wss://{origin}/copilot/ws/tasks/{taskId}?token=JWT` (não passa por
|
|
@@ -694,7 +694,8 @@ declare function getAgentTaskMitra(options: GetAgentTaskOptions): AgentTaskSessi
|
|
|
694
694
|
* Mitra Interactions SDK — Agent Credentials (via BFF)
|
|
695
695
|
*
|
|
696
696
|
* Função ÚNICA `manageAgentCredentialMitra`, toda HTTP pela BFF no baseURL
|
|
697
|
-
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e
|
|
697
|
+
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e a troca de token
|
|
698
|
+
* server-side:
|
|
698
699
|
* - list_providers → GET /agentAiShortcut/listProviders?projectId=
|
|
699
700
|
* - list_models → GET /agentAiShortcut/listModels?projectId=&agentId= (agentId opcional)
|
|
700
701
|
* - save → POST /agentAiShortcut/saveCredential {projectId, provider, key}
|
package/dist/index.d.ts
CHANGED
|
@@ -651,8 +651,8 @@ declare function refreshTokenSilently(authUrl: string, _projectId: number | stri
|
|
|
651
651
|
* Mitra Interactions SDK — Agent Chat (via BFF)
|
|
652
652
|
*
|
|
653
653
|
* REST-first, tudo pela BFF no baseURL normal (`/agentAiShortcut/*`) — herda
|
|
654
|
-
* fetchWithRefresh e
|
|
655
|
-
* desembrulhadas do Page (result é o array direto).
|
|
654
|
+
* fetchWithRefresh e a troca de token server-side. Envelope `{ status, result }`;
|
|
655
|
+
* listas vêm desembrulhadas do Page (result é o array direto).
|
|
656
656
|
*
|
|
657
657
|
* SÓ o prompt é streaming: WebSocket por task em
|
|
658
658
|
* `wss://{origin}/copilot/ws/tasks/{taskId}?token=JWT` (não passa por
|
|
@@ -694,7 +694,8 @@ declare function getAgentTaskMitra(options: GetAgentTaskOptions): AgentTaskSessi
|
|
|
694
694
|
* Mitra Interactions SDK — Agent Credentials (via BFF)
|
|
695
695
|
*
|
|
696
696
|
* Função ÚNICA `manageAgentCredentialMitra`, toda HTTP pela BFF no baseURL
|
|
697
|
-
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e
|
|
697
|
+
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e a troca de token
|
|
698
|
+
* server-side:
|
|
698
699
|
* - list_providers → GET /agentAiShortcut/listProviders?projectId=
|
|
699
700
|
* - list_models → GET /agentAiShortcut/listModels?projectId=&agentId= (agentId opcional)
|
|
700
701
|
* - save → POST /agentAiShortcut/saveCredential {projectId, provider, key}
|
package/dist/index.js
CHANGED
|
@@ -498,6 +498,8 @@ var httpTenant = {
|
|
|
498
498
|
var QUEUE_MAX = 10;
|
|
499
499
|
var DEFAULT_AGENT_TYPE = "ANTHROPIC_CLAUDE_OPUS";
|
|
500
500
|
var CANCEL_SAFETY_MS = 1e4;
|
|
501
|
+
var WS_SILENCE_MS = 6e4;
|
|
502
|
+
var WS_RECONNECT_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
501
503
|
var queueSeq = 0;
|
|
502
504
|
var AgentTaskSession = class {
|
|
503
505
|
constructor(init) {
|
|
@@ -512,7 +514,24 @@ var AgentTaskSession = class {
|
|
|
512
514
|
// ── Transporte ────────────────────────
|
|
513
515
|
this._ws = null;
|
|
514
516
|
this._wsConnectPromise = null;
|
|
517
|
+
/** Metadados do chat existente (o `runtime` entre eles). Quem escolhe transporte espera
|
|
518
|
+
* por ela: sem o runtime a session nem pergunta pelo canal do box e cai no WS do copilot,
|
|
519
|
+
* que numa conversa entregue por HTTP não tem como levar a mensagem ao agente. */
|
|
520
|
+
this._metadataReady = null;
|
|
515
521
|
this._creatingTask = false;
|
|
522
|
+
/** O socket atual é o do box (canal direto), que sabe repetir o log por `sequence`. */
|
|
523
|
+
this._wsIsDirect = false;
|
|
524
|
+
/** Já houve socket aberto nesta session: do segundo em diante existe buraco a cobrir. */
|
|
525
|
+
this._wsEverOpened = false;
|
|
526
|
+
/** Onde o log do box estava quando esta session entrou. Congelado no primeiro socket: numa
|
|
527
|
+
* reconexão o canal já conta o que a session perdeu, e adotar o número de lá pularia
|
|
528
|
+
* justamente esses eventos. */
|
|
529
|
+
this._joinSequence = 0;
|
|
530
|
+
/** Maior `sequence` já vista nesta session: de onde o box repete depois de uma queda. */
|
|
531
|
+
this._lastSequence = 0;
|
|
532
|
+
this._wsSilenceTimer = null;
|
|
533
|
+
this._wsReconnectTimer = null;
|
|
534
|
+
this._wsReconnectAttempt = 0;
|
|
516
535
|
// ── Cancel ────────────────────────────
|
|
517
536
|
this._cancelTimer = null;
|
|
518
537
|
// ── Transporte HTTP (SSE por fetch; o WS continua o default) ───────────
|
|
@@ -564,11 +583,15 @@ var AgentTaskSession = class {
|
|
|
564
583
|
}
|
|
565
584
|
// ── Abertura de chat existente ─────────
|
|
566
585
|
async _openExisting(taskId) {
|
|
567
|
-
|
|
586
|
+
this._metadataReady = (async () => {
|
|
568
587
|
const t = await readChatTask(taskId, this._projectId);
|
|
569
588
|
this._task = toAgentChat(t);
|
|
589
|
+
this._runtime = t.runtime;
|
|
570
590
|
this._agentType = this._task.agentType;
|
|
571
591
|
if (this._status === "opening") this._setStatus("idle");
|
|
592
|
+
})();
|
|
593
|
+
try {
|
|
594
|
+
await this._metadataReady;
|
|
572
595
|
if (this._transport === "http") await this._ensureSse();
|
|
573
596
|
else await this._ensureWs();
|
|
574
597
|
} catch (err) {
|
|
@@ -625,6 +648,7 @@ var AgentTaskSession = class {
|
|
|
625
648
|
autonomous: this._autonomous
|
|
626
649
|
});
|
|
627
650
|
this._task = toAgentChat(t);
|
|
651
|
+
this._runtime = t.runtime;
|
|
628
652
|
this._taskId = this._task.id;
|
|
629
653
|
registerSession(this._taskId, this);
|
|
630
654
|
this._emit("taskCreated", { task: this._task });
|
|
@@ -698,6 +722,7 @@ var AgentTaskSession = class {
|
|
|
698
722
|
clearTimeout(this._cancelTimer);
|
|
699
723
|
this._cancelTimer = null;
|
|
700
724
|
}
|
|
725
|
+
this._clearWsTimers();
|
|
701
726
|
if (this._ws) {
|
|
702
727
|
try {
|
|
703
728
|
this._ws.close();
|
|
@@ -860,13 +885,15 @@ var AgentTaskSession = class {
|
|
|
860
885
|
}
|
|
861
886
|
this._wsConnectPromise = (async () => {
|
|
862
887
|
try {
|
|
888
|
+
if (this._metadataReady) await this._metadataReady.catch(() => {
|
|
889
|
+
});
|
|
863
890
|
if (isTokenExpiring()) await tryRefreshToken();
|
|
864
891
|
try {
|
|
865
892
|
return await this._connectWs(this._taskId);
|
|
866
893
|
} catch (err) {
|
|
867
894
|
const refreshed = await tryRefreshToken();
|
|
868
|
-
if (!refreshed) throw err;
|
|
869
|
-
return await this._connectWs(this._taskId);
|
|
895
|
+
if (!refreshed && this._runtime !== "T3") throw err;
|
|
896
|
+
return await this._connectWs(this._taskId, false);
|
|
870
897
|
}
|
|
871
898
|
} finally {
|
|
872
899
|
this._wsConnectPromise = null;
|
|
@@ -874,8 +901,14 @@ var AgentTaskSession = class {
|
|
|
874
901
|
})();
|
|
875
902
|
return this._wsConnectPromise;
|
|
876
903
|
}
|
|
877
|
-
|
|
878
|
-
|
|
904
|
+
/** `allowDirect` false força o WS do copilot: é como a reconexão degrada um box que não abriu. */
|
|
905
|
+
async _connectWs(taskId, allowDirect = true) {
|
|
906
|
+
var _a;
|
|
907
|
+
const direct = allowDirect && this._runtime === "T3" ? await requestDirectChannel(taskId, () => this._status === "closed") : null;
|
|
908
|
+
if (this._status === "closed") {
|
|
909
|
+
throw new Error("AgentTaskSession: session fechada durante a conex\xE3o.");
|
|
910
|
+
}
|
|
911
|
+
const url = (_a = direct == null ? void 0 : direct.wsUrl) != null ? _a : getTaskWsUrl(taskId);
|
|
879
912
|
return new Promise((resolve, reject) => {
|
|
880
913
|
const socket = new WebSocket(url);
|
|
881
914
|
let opened = false;
|
|
@@ -891,7 +924,28 @@ var AgentTaskSession = class {
|
|
|
891
924
|
socket.onopen = () => {
|
|
892
925
|
opened = true;
|
|
893
926
|
clearTimeout(timer);
|
|
927
|
+
if (this._status === "closed") {
|
|
928
|
+
try {
|
|
929
|
+
socket.close();
|
|
930
|
+
} catch (e) {
|
|
931
|
+
}
|
|
932
|
+
reject(new Error("AgentTaskSession: session fechada durante a conex\xE3o."));
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
894
935
|
this._ws = socket;
|
|
936
|
+
this._wsIsDirect = direct !== null;
|
|
937
|
+
this._wsReconnectAttempt = 0;
|
|
938
|
+
this._armSilenceWatchdog(socket);
|
|
939
|
+
const reconexao = this._wsEverOpened;
|
|
940
|
+
if (!reconexao && direct) this._joinSequence = direct.lastSequence;
|
|
941
|
+
this._wsEverOpened = true;
|
|
942
|
+
if (this._wsIsDirect && reconexao) {
|
|
943
|
+
const fromSequence = Math.max(this._lastSequence, this._joinSequence);
|
|
944
|
+
try {
|
|
945
|
+
socket.send(JSON.stringify({ type: "replay", fromSequence }));
|
|
946
|
+
} catch (e) {
|
|
947
|
+
}
|
|
948
|
+
}
|
|
895
949
|
resolve(socket);
|
|
896
950
|
};
|
|
897
951
|
socket.onerror = () => {
|
|
@@ -901,9 +955,13 @@ var AgentTaskSession = class {
|
|
|
901
955
|
}
|
|
902
956
|
};
|
|
903
957
|
socket.onclose = () => {
|
|
904
|
-
if (this._ws
|
|
958
|
+
if (this._ws !== socket) return;
|
|
959
|
+
this._ws = null;
|
|
960
|
+
this._clearSilenceWatchdog();
|
|
961
|
+
if (this._status !== "closed") this._scheduleReconnect();
|
|
905
962
|
};
|
|
906
963
|
socket.onmessage = (evt) => {
|
|
964
|
+
if (this._ws === socket) this._armSilenceWatchdog(socket);
|
|
907
965
|
let msg;
|
|
908
966
|
try {
|
|
909
967
|
msg = JSON.parse(String(evt.data));
|
|
@@ -914,13 +972,68 @@ var AgentTaskSession = class {
|
|
|
914
972
|
};
|
|
915
973
|
});
|
|
916
974
|
}
|
|
917
|
-
// ──
|
|
975
|
+
// ── Vida do socket: watchdog de silêncio e reconexão ──────────────────
|
|
976
|
+
_armSilenceWatchdog(socket) {
|
|
977
|
+
this._clearSilenceWatchdog();
|
|
978
|
+
this._wsSilenceTimer = setTimeout(() => {
|
|
979
|
+
this._wsSilenceTimer = null;
|
|
980
|
+
if (this._ws !== socket) return;
|
|
981
|
+
try {
|
|
982
|
+
socket.close();
|
|
983
|
+
} catch (e) {
|
|
984
|
+
}
|
|
985
|
+
if (this._ws === socket) {
|
|
986
|
+
this._ws = null;
|
|
987
|
+
if (this._status !== "closed") this._scheduleReconnect();
|
|
988
|
+
}
|
|
989
|
+
}, WS_SILENCE_MS);
|
|
990
|
+
}
|
|
991
|
+
_clearSilenceWatchdog() {
|
|
992
|
+
if (this._wsSilenceTimer) {
|
|
993
|
+
clearTimeout(this._wsSilenceTimer);
|
|
994
|
+
this._wsSilenceTimer = null;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
_clearWsTimers() {
|
|
998
|
+
this._clearSilenceWatchdog();
|
|
999
|
+
if (this._wsReconnectTimer) {
|
|
1000
|
+
clearTimeout(this._wsReconnectTimer);
|
|
1001
|
+
this._wsReconnectTimer = null;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
_scheduleReconnect() {
|
|
1005
|
+
if (this._wsReconnectTimer || this._wsConnectPromise) return;
|
|
1006
|
+
const delay = WS_RECONNECT_DELAYS_MS[this._wsReconnectAttempt];
|
|
1007
|
+
if (delay === void 0) {
|
|
1008
|
+
this._wsReconnectAttempt = 0;
|
|
1009
|
+
this._emit("error", { code: "CONNECTION_LOST", error: "Conex\xE3o com o agente perdida; a resposta segue no hist\xF3rico." });
|
|
1010
|
+
if (this._status === "streaming" || this._status === "cancelled") this._setStatus("idle");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
this._wsReconnectAttempt += 1;
|
|
1014
|
+
this._wsReconnectTimer = setTimeout(() => {
|
|
1015
|
+
this._wsReconnectTimer = null;
|
|
1016
|
+
if (this._status === "closed") return;
|
|
1017
|
+
void this._ensureWs().catch(() => {
|
|
1018
|
+
if (this._status !== "closed") this._scheduleReconnect();
|
|
1019
|
+
});
|
|
1020
|
+
}, delay);
|
|
1021
|
+
}
|
|
1022
|
+
// ── Eventos do stream (WsOutboundMessage { type, payload, timestamp, sequence? }) ──
|
|
918
1023
|
_handleWsEvent(msg) {
|
|
919
1024
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
920
1025
|
if (!msg || typeof msg.type !== "string" || !msg.type) return;
|
|
1026
|
+
if (typeof msg.sequence === "number" && msg.sequence > this._lastSequence) {
|
|
1027
|
+
this._lastSequence = msg.sequence;
|
|
1028
|
+
}
|
|
921
1029
|
const { type, payload } = msg;
|
|
922
1030
|
switch (type) {
|
|
923
|
-
|
|
1031
|
+
// `textChunk` é a forma que o texto do agente tem no log do box: ele transmite ao vivo
|
|
1032
|
+
// em `textDelta` e dobra os deltas em chunks numerados ao gravar. Uma repetição depois
|
|
1033
|
+
// de queda devolve chunks, então tratá-los como texto é o que faz a resposta recuperada
|
|
1034
|
+
// aparecer; sem este caso ela chega no socket e é descartada em silêncio.
|
|
1035
|
+
case "textDelta":
|
|
1036
|
+
case "textChunk": {
|
|
924
1037
|
const text = (_a = payload == null ? void 0 : payload.text) != null ? _a : "";
|
|
925
1038
|
if (this._status !== "streaming" && this._status !== "cancelled") this._setStatus("streaming");
|
|
926
1039
|
this._content += text;
|
|
@@ -1099,6 +1212,59 @@ function getRawToken() {
|
|
|
1099
1212
|
}
|
|
1100
1213
|
return stripBearer(config.token);
|
|
1101
1214
|
}
|
|
1215
|
+
var CHANNEL_BOOT_RETRY_MS = 2e3;
|
|
1216
|
+
var CHANNEL_BOOT_TIMEOUT_MS = 12e4;
|
|
1217
|
+
function sleep(ms) {
|
|
1218
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1219
|
+
}
|
|
1220
|
+
function isGatewayWsUrl(wsUrl) {
|
|
1221
|
+
const expected = getGatewayOrigin().replace(/^http/, "ws");
|
|
1222
|
+
try {
|
|
1223
|
+
return new URL(wsUrl).origin === expected;
|
|
1224
|
+
} catch (e) {
|
|
1225
|
+
return false;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
async function requestDirectChannel(taskId, isCancelled) {
|
|
1229
|
+
var _a;
|
|
1230
|
+
if (!hasAppScope(getRawToken())) return null;
|
|
1231
|
+
const url = `${getGatewayOrigin()}/copilot/api/v1/tasks/${encodeURIComponent(taskId)}/channel`;
|
|
1232
|
+
const doPost = async () => fetch(url, {
|
|
1233
|
+
method: "POST",
|
|
1234
|
+
headers: { Authorization: `Bearer ${getRawToken()}` }
|
|
1235
|
+
});
|
|
1236
|
+
const deadline = Date.now() + CHANNEL_BOOT_TIMEOUT_MS;
|
|
1237
|
+
for (; ; ) {
|
|
1238
|
+
if (isCancelled == null ? void 0 : isCancelled()) return null;
|
|
1239
|
+
let response;
|
|
1240
|
+
try {
|
|
1241
|
+
response = await doPost();
|
|
1242
|
+
if (response.status === 401 && await tryRefreshToken()) response = await doPost();
|
|
1243
|
+
} catch (e) {
|
|
1244
|
+
return null;
|
|
1245
|
+
}
|
|
1246
|
+
if (response.status === 202) {
|
|
1247
|
+
if (Date.now() >= deadline) return null;
|
|
1248
|
+
await sleep(CHANNEL_BOOT_RETRY_MS);
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
if (response.status !== 200) return null;
|
|
1252
|
+
let body;
|
|
1253
|
+
try {
|
|
1254
|
+
body = await response.json();
|
|
1255
|
+
} catch (e) {
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
const channel = body;
|
|
1259
|
+
if (!channel || typeof channel.wsUrl !== "string" || !channel.wsUrl) return null;
|
|
1260
|
+
if (!isGatewayWsUrl(channel.wsUrl)) return null;
|
|
1261
|
+
return {
|
|
1262
|
+
wsUrl: channel.wsUrl,
|
|
1263
|
+
expiresAt: String((_a = channel.expiresAt) != null ? _a : ""),
|
|
1264
|
+
lastSequence: typeof channel.lastSequence === "number" ? channel.lastSequence : 0
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1102
1268
|
function toAgentChat(t) {
|
|
1103
1269
|
return {
|
|
1104
1270
|
id: t.id,
|