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 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?, transport?, userId? })` ou `getAgentTaskMitra({ taskId, transport? })`.
304
+ `getAgentTaskMitra({ create: true, agentType?, name?, agentId?, reasoningEffort?, autonomous?, userId?, transport? })` ou `getAgentTaskMitra({ taskId, transport? })`.
305
305
 
306
- **Agente autônomo**: a autonomia é **propriedade do AGENTE** (`autonomous` em `createAgentMitra`/`updateAgentMitra`, no `mitra-sdk`) — não existe flag na criação do chat. Ao criar um chat contra um agente autônomo (`getAgentTaskMitra({ create: true, agentId })`), o copilot deriva a autonomia do agente e o chat já nasce **sem dono** (`user_id NULL` — a dona é o agente), usando a connection anexada ao agente como credencial. Exige auth `AGENT_WRITE` (chave de SF ou token de app EDIT) — usuário business comum não abre chat autônomo. Depois de criado, dirigir/listar é igual ao chat normal (`getAgentTaskMitra({ taskId })`, `send`, `manageAgentChatMitra({ list, agentId })`).
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 o CORS do BFF. Envelope `{ status, result }`; listas vêm
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 o CORS do BFF:
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 o CORS do BFF. Envelope `{ status, result }`; listas vêm
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 o CORS do BFF:
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
- try {
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
- _connectWs(taskId) {
876
- const url = getTaskWsUrl(taskId);
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 === socket) this._ws = null;
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
- // ── Eventos do stream (WsOutboundMessage { type, payload, timestamp }) ──
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
- case "textDelta": {
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 AGENTE (createAgentMitra/updateAgentMitra no
1117
- // mitra-sdk): o copilot deriva do agentId — o chat de agente autônomo já
1118
- // nasce ownerless sem flag nenhum aqui.
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 + agente autônomo é contradição).
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 }.");