mitra-interactions-sdk 1.0.60-beta.47 → 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 +3 -3
- package/dist/index.d.mts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +187 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +187 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
|
@@ -301,9 +301,9 @@ const existing = getAgentTaskMitra({ taskId: 'uuid-da-task' });
|
|
|
301
301
|
await existing.loadHistory({ limit: 50 });
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
-
`getAgentTaskMitra({ create: true, agentType?, name?, agentId?, reasoningEffort?,
|
|
304
|
+
`getAgentTaskMitra({ create: true, agentType?, name?, agentId?, reasoningEffort?, autonomous?, userId?, transport? })` ou `getAgentTaskMitra({ taskId, transport? })`.
|
|
305
305
|
|
|
306
|
-
**
|
|
306
|
+
**Chat autônomo (`autonomous: true`)**: cria o chat **sem dono** (`user_id NULL` — a dona é o agente), pra Server Function/webhook/cron dirigir o agente. A autonomia é **propriedade do CHAT** — o flag `autonomous` do agente não tem mais leitor no backend. Exige `agentId` (fail-fast no client) e o agente precisa ter uma **connection anexada** como credencial (senão `400 AUTONOMOUS_TASK_REQUIRES_CONNECTION`); auth `AGENT_WRITE` (chave de SF ou token de app EDIT), senão 403. Depois de criado, dirigir/listar é igual ao chat normal (`getAgentTaskMitra({ taskId })`, `send`, `manageAgentChatMitra({ list, agentId })`). Sem o flag, comportamento de sempre (chat com dono = o caller).
|
|
307
307
|
|
|
308
308
|
- `agentType` vem de `manageAgentCredentialMitra({ action: 'list_models' })` — ex.: `'ANTHROPIC_CLAUDE_OPUS'`, `'OPENAI_GPT5'`. Default: `'ANTHROPIC_CLAUDE_OPUS'`.
|
|
309
309
|
- **`agentId`**: id de um agente business (CRUD via `mitra-sdk`: `listAgentsMitra` e família). A sessão sobe com o system prompt do agente e um token escopado — as tools enxergam só as Server Functions dele. **Sem `agentId`: sessão business sem agente** — sem system prompt e sem acesso às Server Functions (as tools recusam); não há adoção automática de agente único. Na prática, sempre passe `agentId`.
|
package/dist/index.d.mts
CHANGED
|
@@ -333,6 +333,17 @@ interface GetAgentTaskCreateOptions {
|
|
|
333
333
|
* ela age.
|
|
334
334
|
*/
|
|
335
335
|
userId?: string;
|
|
336
|
+
/**
|
|
337
|
+
* Cria o chat SEM dono (user_id NULL — a dona é o agente): a modalidade
|
|
338
|
+
* AUTÔNOMA, onde uma Server Function / webhook / cron dirige o agente.
|
|
339
|
+
* Exige `agentId` e o agente precisa ter uma connection anexada (é a
|
|
340
|
+
* credencial do chat); auth AGENT_WRITE (chave de SF), senão 403. Agente
|
|
341
|
+
* sem connection: 400 AUTONOMOUS_TASK_REQUIRES_CONNECTION. Mutuamente
|
|
342
|
+
* exclusivo com `userId` (400 AUTONOMOUS_TASK_HAS_NO_OWNER).
|
|
343
|
+
* Omitido = chat com dono (o caller), comportamento de sempre.
|
|
344
|
+
* A autonomia é do CHAT: o flag `autonomous` do AGENTE não tem leitor.
|
|
345
|
+
*/
|
|
346
|
+
autonomous?: boolean;
|
|
336
347
|
/** Transporte do prompt (default 'ws'). */
|
|
337
348
|
transport?: AgentTaskTransport;
|
|
338
349
|
}
|
|
@@ -640,8 +651,8 @@ declare function refreshTokenSilently(authUrl: string, _projectId: number | stri
|
|
|
640
651
|
* Mitra Interactions SDK — Agent Chat (via BFF)
|
|
641
652
|
*
|
|
642
653
|
* REST-first, tudo pela BFF no baseURL normal (`/agentAiShortcut/*`) — herda
|
|
643
|
-
* fetchWithRefresh e
|
|
644
|
-
* 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).
|
|
645
656
|
*
|
|
646
657
|
* SÓ o prompt é streaming: WebSocket por task em
|
|
647
658
|
* `wss://{origin}/copilot/ws/tasks/{taskId}?token=JWT` (não passa por
|
|
@@ -683,7 +694,8 @@ declare function getAgentTaskMitra(options: GetAgentTaskOptions): AgentTaskSessi
|
|
|
683
694
|
* Mitra Interactions SDK — Agent Credentials (via BFF)
|
|
684
695
|
*
|
|
685
696
|
* Função ÚNICA `manageAgentCredentialMitra`, toda HTTP pela BFF no baseURL
|
|
686
|
-
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e
|
|
697
|
+
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e a troca de token
|
|
698
|
+
* server-side:
|
|
687
699
|
* - list_providers → GET /agentAiShortcut/listProviders?projectId=
|
|
688
700
|
* - list_models → GET /agentAiShortcut/listModels?projectId=&agentId= (agentId opcional)
|
|
689
701
|
* - save → POST /agentAiShortcut/saveCredential {projectId, provider, key}
|
package/dist/index.d.ts
CHANGED
|
@@ -333,6 +333,17 @@ interface GetAgentTaskCreateOptions {
|
|
|
333
333
|
* ela age.
|
|
334
334
|
*/
|
|
335
335
|
userId?: string;
|
|
336
|
+
/**
|
|
337
|
+
* Cria o chat SEM dono (user_id NULL — a dona é o agente): a modalidade
|
|
338
|
+
* AUTÔNOMA, onde uma Server Function / webhook / cron dirige o agente.
|
|
339
|
+
* Exige `agentId` e o agente precisa ter uma connection anexada (é a
|
|
340
|
+
* credencial do chat); auth AGENT_WRITE (chave de SF), senão 403. Agente
|
|
341
|
+
* sem connection: 400 AUTONOMOUS_TASK_REQUIRES_CONNECTION. Mutuamente
|
|
342
|
+
* exclusivo com `userId` (400 AUTONOMOUS_TASK_HAS_NO_OWNER).
|
|
343
|
+
* Omitido = chat com dono (o caller), comportamento de sempre.
|
|
344
|
+
* A autonomia é do CHAT: o flag `autonomous` do AGENTE não tem leitor.
|
|
345
|
+
*/
|
|
346
|
+
autonomous?: boolean;
|
|
336
347
|
/** Transporte do prompt (default 'ws'). */
|
|
337
348
|
transport?: AgentTaskTransport;
|
|
338
349
|
}
|
|
@@ -640,8 +651,8 @@ declare function refreshTokenSilently(authUrl: string, _projectId: number | stri
|
|
|
640
651
|
* Mitra Interactions SDK — Agent Chat (via BFF)
|
|
641
652
|
*
|
|
642
653
|
* REST-first, tudo pela BFF no baseURL normal (`/agentAiShortcut/*`) — herda
|
|
643
|
-
* fetchWithRefresh e
|
|
644
|
-
* 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).
|
|
645
656
|
*
|
|
646
657
|
* SÓ o prompt é streaming: WebSocket por task em
|
|
647
658
|
* `wss://{origin}/copilot/ws/tasks/{taskId}?token=JWT` (não passa por
|
|
@@ -683,7 +694,8 @@ declare function getAgentTaskMitra(options: GetAgentTaskOptions): AgentTaskSessi
|
|
|
683
694
|
* Mitra Interactions SDK — Agent Credentials (via BFF)
|
|
684
695
|
*
|
|
685
696
|
* Função ÚNICA `manageAgentCredentialMitra`, toda HTTP pela BFF no baseURL
|
|
686
|
-
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e
|
|
697
|
+
* normal (`/agentAiShortcut/*`) — herda fetchWithRefresh e a troca de token
|
|
698
|
+
* server-side:
|
|
687
699
|
* - list_providers → GET /agentAiShortcut/listProviders?projectId=
|
|
688
700
|
* - list_models → GET /agentAiShortcut/listModels?projectId=&agentId= (agentId opcional)
|
|
689
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) ───────────
|
|
@@ -531,6 +550,7 @@ var AgentTaskSession = class {
|
|
|
531
550
|
this._initialName = init.name;
|
|
532
551
|
this._agentId = init.agentId;
|
|
533
552
|
this._reasoningEffort = init.reasoningEffort;
|
|
553
|
+
this._autonomous = init.autonomous;
|
|
534
554
|
this._status = "idle";
|
|
535
555
|
} else {
|
|
536
556
|
this._agentType = DEFAULT_AGENT_TYPE;
|
|
@@ -563,11 +583,15 @@ var AgentTaskSession = class {
|
|
|
563
583
|
}
|
|
564
584
|
// ── Abertura de chat existente ─────────
|
|
565
585
|
async _openExisting(taskId) {
|
|
566
|
-
|
|
586
|
+
this._metadataReady = (async () => {
|
|
567
587
|
const t = await readChatTask(taskId, this._projectId);
|
|
568
588
|
this._task = toAgentChat(t);
|
|
589
|
+
this._runtime = t.runtime;
|
|
569
590
|
this._agentType = this._task.agentType;
|
|
570
591
|
if (this._status === "opening") this._setStatus("idle");
|
|
592
|
+
})();
|
|
593
|
+
try {
|
|
594
|
+
await this._metadataReady;
|
|
571
595
|
if (this._transport === "http") await this._ensureSse();
|
|
572
596
|
else await this._ensureWs();
|
|
573
597
|
} catch (err) {
|
|
@@ -620,9 +644,11 @@ var AgentTaskSession = class {
|
|
|
620
644
|
title: this._initialName,
|
|
621
645
|
agentType: this._agentType,
|
|
622
646
|
agentId: this._agentId,
|
|
623
|
-
userId: this._userId
|
|
647
|
+
userId: this._userId,
|
|
648
|
+
autonomous: this._autonomous
|
|
624
649
|
});
|
|
625
650
|
this._task = toAgentChat(t);
|
|
651
|
+
this._runtime = t.runtime;
|
|
626
652
|
this._taskId = this._task.id;
|
|
627
653
|
registerSession(this._taskId, this);
|
|
628
654
|
this._emit("taskCreated", { task: this._task });
|
|
@@ -696,6 +722,7 @@ var AgentTaskSession = class {
|
|
|
696
722
|
clearTimeout(this._cancelTimer);
|
|
697
723
|
this._cancelTimer = null;
|
|
698
724
|
}
|
|
725
|
+
this._clearWsTimers();
|
|
699
726
|
if (this._ws) {
|
|
700
727
|
try {
|
|
701
728
|
this._ws.close();
|
|
@@ -858,13 +885,15 @@ var AgentTaskSession = class {
|
|
|
858
885
|
}
|
|
859
886
|
this._wsConnectPromise = (async () => {
|
|
860
887
|
try {
|
|
888
|
+
if (this._metadataReady) await this._metadataReady.catch(() => {
|
|
889
|
+
});
|
|
861
890
|
if (isTokenExpiring()) await tryRefreshToken();
|
|
862
891
|
try {
|
|
863
892
|
return await this._connectWs(this._taskId);
|
|
864
893
|
} catch (err) {
|
|
865
894
|
const refreshed = await tryRefreshToken();
|
|
866
|
-
if (!refreshed) throw err;
|
|
867
|
-
return await this._connectWs(this._taskId);
|
|
895
|
+
if (!refreshed && this._runtime !== "T3") throw err;
|
|
896
|
+
return await this._connectWs(this._taskId, false);
|
|
868
897
|
}
|
|
869
898
|
} finally {
|
|
870
899
|
this._wsConnectPromise = null;
|
|
@@ -872,8 +901,14 @@ var AgentTaskSession = class {
|
|
|
872
901
|
})();
|
|
873
902
|
return this._wsConnectPromise;
|
|
874
903
|
}
|
|
875
|
-
|
|
876
|
-
|
|
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);
|
|
877
912
|
return new Promise((resolve, reject) => {
|
|
878
913
|
const socket = new WebSocket(url);
|
|
879
914
|
let opened = false;
|
|
@@ -889,7 +924,28 @@ var AgentTaskSession = class {
|
|
|
889
924
|
socket.onopen = () => {
|
|
890
925
|
opened = true;
|
|
891
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
|
+
}
|
|
892
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
|
+
}
|
|
893
949
|
resolve(socket);
|
|
894
950
|
};
|
|
895
951
|
socket.onerror = () => {
|
|
@@ -899,9 +955,13 @@ var AgentTaskSession = class {
|
|
|
899
955
|
}
|
|
900
956
|
};
|
|
901
957
|
socket.onclose = () => {
|
|
902
|
-
if (this._ws
|
|
958
|
+
if (this._ws !== socket) return;
|
|
959
|
+
this._ws = null;
|
|
960
|
+
this._clearSilenceWatchdog();
|
|
961
|
+
if (this._status !== "closed") this._scheduleReconnect();
|
|
903
962
|
};
|
|
904
963
|
socket.onmessage = (evt) => {
|
|
964
|
+
if (this._ws === socket) this._armSilenceWatchdog(socket);
|
|
905
965
|
let msg;
|
|
906
966
|
try {
|
|
907
967
|
msg = JSON.parse(String(evt.data));
|
|
@@ -912,13 +972,68 @@ var AgentTaskSession = class {
|
|
|
912
972
|
};
|
|
913
973
|
});
|
|
914
974
|
}
|
|
915
|
-
// ──
|
|
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? }) ──
|
|
916
1023
|
_handleWsEvent(msg) {
|
|
917
1024
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
918
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
|
+
}
|
|
919
1029
|
const { type, payload } = msg;
|
|
920
1030
|
switch (type) {
|
|
921
|
-
|
|
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": {
|
|
922
1037
|
const text = (_a = payload == null ? void 0 : payload.text) != null ? _a : "";
|
|
923
1038
|
if (this._status !== "streaming" && this._status !== "cancelled") this._setStatus("streaming");
|
|
924
1039
|
this._content += text;
|
|
@@ -1097,6 +1212,59 @@ function getRawToken() {
|
|
|
1097
1212
|
}
|
|
1098
1213
|
return stripBearer(config.token);
|
|
1099
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
|
+
}
|
|
1100
1268
|
function toAgentChat(t) {
|
|
1101
1269
|
return {
|
|
1102
1270
|
id: t.id,
|
|
@@ -1113,15 +1281,16 @@ async function createChatTask(options) {
|
|
|
1113
1281
|
projectId: resolveProjectId(options.projectId),
|
|
1114
1282
|
...options.title ? { title: options.title } : {},
|
|
1115
1283
|
agentType: options.agentType,
|
|
1116
|
-
// Autonomia é propriedade do
|
|
1117
|
-
//
|
|
1118
|
-
//
|
|
1284
|
+
// Autonomia é propriedade do CHAT: autonomous: true cria a task SEM dono
|
|
1285
|
+
// (a dona é o agente; exige connection anexada e AGENT_WRITE). O flag no
|
|
1286
|
+
// AGENTE não tem mais leitor no backend.
|
|
1119
1287
|
...options.agentId ? { agentId: options.agentId } : {},
|
|
1288
|
+
...options.autonomous ? { autonomous: true } : {},
|
|
1120
1289
|
// Agir EM NOME do dono (SF/webhook/cron com AGENT_WRITE): o chat nasce
|
|
1121
1290
|
// pertencendo a esse usuário; o turno roda com a identidade/credencial
|
|
1122
1291
|
// DELE. Ausente = chat do caller, byte a byte como sempre.
|
|
1123
1292
|
// Erros do backend passam crus: 403 (sem AGENT_WRITE),
|
|
1124
|
-
// 400 AUTONOMOUS_TASK_HAS_NO_OWNER (userId +
|
|
1293
|
+
// 400 AUTONOMOUS_TASK_HAS_NO_OWNER (userId + autonomous é contradição).
|
|
1125
1294
|
...options.userId ? { userId: options.userId } : {}
|
|
1126
1295
|
});
|
|
1127
1296
|
return env.result;
|
|
@@ -1198,6 +1367,9 @@ function getAgentTaskMitra(options) {
|
|
|
1198
1367
|
});
|
|
1199
1368
|
}
|
|
1200
1369
|
if ("create" in options && options.create) {
|
|
1370
|
+
if (options.autonomous && !options.agentId) {
|
|
1371
|
+
throw new Error("getAgentTaskMitra: autonomous exige agentId \u2014 o chat aut\xF4nomo pertence ao agente (e o agente precisa ter uma connection anexada).");
|
|
1372
|
+
}
|
|
1201
1373
|
return new AgentTaskSession({
|
|
1202
1374
|
kind: "new",
|
|
1203
1375
|
projectId: options.projectId,
|
|
@@ -1206,7 +1378,8 @@ function getAgentTaskMitra(options) {
|
|
|
1206
1378
|
agentId: options.agentId,
|
|
1207
1379
|
transport: options.transport,
|
|
1208
1380
|
reasoningEffort: options.reasoningEffort,
|
|
1209
|
-
userId: options.userId
|
|
1381
|
+
userId: options.userId,
|
|
1382
|
+
autonomous: options.autonomous
|
|
1210
1383
|
});
|
|
1211
1384
|
}
|
|
1212
1385
|
throw new Error("getAgentTaskMitra: passe { create: true } ou { taskId }.");
|