@smoothcodes/agent-client 0.2.3 → 0.3.0

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/index.js CHANGED
@@ -1,16 +1,20 @@
1
1
  import {
2
+ CommandAbortedError,
2
3
  DEFAULT_BASE_URL,
3
4
  ProviderSet,
5
+ SendNotConfirmedError,
4
6
  SmoothAgent,
5
7
  SmoothAgentApiError,
6
8
  SmoothAgentApiTimeoutError,
7
9
  apiErrorBody,
8
10
  sortProviders
9
- } from "./chunk-ERJ5SQDU.js";
11
+ } from "./chunk-4HQLFTS2.js";
10
12
  import "./chunk-PZ5AY32C.js";
11
13
  export {
14
+ CommandAbortedError,
12
15
  DEFAULT_BASE_URL,
13
16
  ProviderSet,
17
+ SendNotConfirmedError,
14
18
  SmoothAgent,
15
19
  SmoothAgentApiError,
16
20
  SmoothAgentApiTimeoutError,
package/dist/testing.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SmoothAgent
3
- } from "./chunk-ERJ5SQDU.js";
3
+ } from "./chunk-4HQLFTS2.js";
4
4
  import "./chunk-PZ5AY32C.js";
5
5
 
6
6
  // src/testing.ts
@@ -4496,6 +4496,14 @@ export interface BLCredentialsFetch {
4496
4496
  * geração que a box já tem
4497
4497
  */
4498
4498
  have?: number;
4499
+ /**
4500
+ * o credentialRef que a box tem agora (sem ele, o control devolve sempre a credencial completa)
4501
+ */
4502
+ haveRef?: string;
4503
+ /**
4504
+ * a família da credencial que a box tem — ref ou família diferente da atual = devolve a completa, qualquer que seja a geração
4505
+ */
4506
+ haveFamily?: string;
4499
4507
  agentId?: AgentId;
4500
4508
  }
4501
4509
  /**
@@ -4618,6 +4626,20 @@ export interface LBWelcome {
4618
4626
  * revisão da config do agente que vale agora (a box compara antes de cada turno)
4619
4627
  */
4620
4628
  agentRevision?: number;
4629
+ /**
4630
+ * A versão ATUAL da credencial deste chat por provedor, no momento do welcome (o corpo do wake + o que o control empurrou depois; fecha a janela do wake assinado com a geração anterior). Antes de subir/usar um provedor no 1º run, a box compara com a dela: geração MENOR ou OUTRO credentialRef = busca SÍNCRONA (credentialsFetch) e grava; a busca falhou OU voltou com geração menor que a do welcome ou outro credentialRef = falha, NÃO roda o turno (falha fechada, nunca um refresh com a credencial velha). Lista PRESENTE sem um provedor = a credencial dele foi revogada/não existe: a box solta a que tem e não roda com ela. `[]` = sabe que não há nenhuma. AUSENTE = não se sabe (realtime antigo, aviso de invalidação perdido, wake sem versões): a box segue a versão do wake, como antes. ⚠️ No Rust gerado (Vec com skip_serializing_if is_empty) `[]` e ausente são indistinguíveis: consumidor Rust trata `[]` como AUSENTE; o Go (ponteiro) e o TS distinguem
4631
+ */
4632
+ credentialVersions?: {
4633
+ provider: Provider;
4634
+ /**
4635
+ * `pcr_…`: o id da credencial no cofre
4636
+ */
4637
+ credentialRef: string;
4638
+ /**
4639
+ * a geração (sobe a cada aceite)
4640
+ */
4641
+ generation: number;
4642
+ }[];
4621
4643
  /**
4622
4644
  * H8: o run que o log ainda vê aberto (a box retoma ou fecha)
4623
4645
  */
@@ -80,3 +80,5 @@ export type { ErrorCode } from '../../core-live/src/types.js';
80
80
  export type { ContractChatState, ContractItem };
81
81
  export { ProviderSet, sortProviders, type ConnectFlow, type ConnectState, type ConnectStatus, type ProviderEntry, type ProviderGroup, type ProviderRow, type ProvidersState } from '../../core-live/src/providers.js';
82
82
  export type { DebugEvent, LiveStatus, RunResult, RunEvent, WatchState, HistoryWindow, NeedsProvider, Violation } from '../../core-live/src/index.js';
83
+ /** O `send` sem confirmação (close sem caminho durável, timeoutMs, ou socket e REST falharam): traz a mensagem pra reenviar. */
84
+ export { SendNotConfirmedError, CommandAbortedError } from '../../core-live/src/index.js';
@@ -72,6 +72,12 @@ export declare class LiveClient<S, I = unknown> {
72
72
  private release;
73
73
  private watchOptions;
74
74
  private deps;
75
+ /**
76
+ * O caminho durável do `send` (REST `SendMessage`): a mesma fila fria do ChatLog, deduplicada pelo
77
+ * `clientMessageId` (= o messageId do socket) e pela Idempotency-Key (a mesma). Falha passageira: repete com
78
+ * espera (`untilStored`) e, esgotado, rejeita; recusa do servidor = `rejected` com o código do §6.
79
+ */
80
+ private postMessage;
75
81
  private json;
76
82
  }
77
83
  /**
@@ -1,5 +1,5 @@
1
1
  export { LiveClient, type LiveClientOptions } from './client.js';
2
- export { LiveChat, newMessageId, uuidv7, type HistoryPage, type HistoryQuery, type HistoryWindow, type LocalSend, type Reducer, type WatchDeps, type SwitchTo, type WatchOptions, type WatchState } from './watch.js';
2
+ export { LiveChat, SendNotConfirmedError, CommandAbortedError, SEND_FALLBACK_MS, newMessageId, uuidv7, type HistoryPage, type HistoryQuery, type HistoryWindow, type LocalSend, type Reducer, type WatchDeps, type SwitchTo, type WatchOptions, type WatchState } from './watch.js';
3
3
  export { RunHandle, PromptRejectedError, type RunEvent, type RunOutcome, type RunResult } from './prompt.js';
4
4
  export { ChatStream, ERROR_CODES, ERROR_CODE_HTTP, errorCode, type ErrorCode, type DebugEvent, type LiveStatus, type StreamListener, type StreamOptions, type Via, type Violation } from './stream.js';
5
5
  export { TokenSource, authedFetch, jwtExpiresAt, SmoothAgentAuthError, type AuthErrorReason } from './auth.js';
@@ -58,7 +58,39 @@ export type HistoryQuery = {
58
58
  });
59
59
  export interface WatchDeps extends LiveDeps {
60
60
  history?(chatId: string, q: HistoryQuery): Promise<HistoryPage>;
61
+ /**
62
+ * REST `POST /v3/chats/{id}/messages` com `clientMessageId` = o messageId do `send` (a MESMA fila fria durável do
63
+ * ChatLog, que deduplica pelo id). O caminho durável do `send` quando o socket não confirma a tempo e no `close()`.
64
+ * Falha passageira: rejeita (o socket segue tentando); recusa do servidor: resolve `rejected`.
65
+ */
66
+ postMessage?(chatId: string, m: {
67
+ clientMessageId: string;
68
+ content: ContentPart[];
69
+ mode: SendMode;
70
+ }): Promise<SubmitResult>;
71
+ }
72
+ /**
73
+ * Um `send` que não chegou a ser confirmado gravado: o `close()` sem caminho durável (`reason: 'closed'`), o
74
+ * `timeoutMs` vencido (`'timeout'`), ou socket e REST falharam (`'failed'`). Traz a mensagem pro app reenviar —
75
+ * reenviar com o MESMO `messageId` é seguro (o servidor deduplica: nunca 2x).
76
+ */
77
+ export declare class SendNotConfirmedError extends Error {
78
+ readonly reason: 'closed' | 'timeout' | 'failed';
79
+ readonly messageId: string;
80
+ readonly content: ContentPart[];
81
+ readonly mode: SendMode;
82
+ readonly cause?: unknown | undefined;
83
+ readonly name = "SendNotConfirmedError";
84
+ constructor(reason: 'closed' | 'timeout' | 'failed', messageId: string, content: ContentPart[], mode: SendMode, cause?: unknown | undefined);
61
85
  }
86
+ /** Um comando (stop/respond/reset/fila/troca) que esperava o ack quando o `close()` chegou. */
87
+ export declare class CommandAbortedError extends Error {
88
+ readonly ref: string;
89
+ readonly name = "CommandAbortedError";
90
+ constructor(ref: string);
91
+ }
92
+ /** Espera padrão do socket antes do caminho durável (REST) no `send`. */
93
+ export declare const SEND_FALLBACK_MS = 2000;
62
94
  export interface LocalSend {
63
95
  messageId: string;
64
96
  content: ContentPart[];
@@ -119,6 +151,11 @@ export interface WatchOptions<S, I> extends Omit<StreamOptions, 'chatId'> {
119
151
  onAuthFailed?(): void;
120
152
  /** Modo estrito: cada violação (também fica em `sync.violations`). */
121
153
  onViolation?(v: Violation): void;
154
+ /**
155
+ * Quanto o `send` espera a confirmação pelo socket antes de gravar pelo REST (`deps.postMessage`), com o mesmo
156
+ * messageId. Padrão 2000 ms; `Infinity` = só no `close()`.
157
+ */
158
+ sendFallbackMs?: number;
122
159
  }
123
160
  type Listener<S, I> = (s: WatchState<S, I>) => void;
124
161
  export declare class LiveChat<S, I = unknown> {
@@ -130,6 +167,17 @@ export declare class LiveChat<S, I = unknown> {
130
167
  private readonly eventTaps;
131
168
  private readonly waiting;
132
169
  private readonly historyWaiting;
170
+ /** os `send` esperando, NA ORDEM em que foram mandados (o Map guarda a ordem de inserção) */
171
+ private readonly sends;
172
+ /** a corrente do REST: um pedido por vez, em ordem (B nunca passa na frente de A) */
173
+ private restBusy;
174
+ private restRetry;
175
+ /** falhas de vez seguidas da corrente (a espera cresce; zera quando uma grava) */
176
+ private restFails;
177
+ /** gravados pelo REST e ainda em `local.sending` do núcleo (que não viu o ack): a tela mostra `pending` */
178
+ private readonly restStored;
179
+ /** o `local.sending` como o núcleo mandou (antes da sobreposição do REST) */
180
+ private coreLocal;
133
181
  /** itens que vieram de páginas do histórico (não estão na timeline viva) */
134
182
  private readonly pageItems;
135
183
  private historyIds;
@@ -144,14 +192,39 @@ export declare class LiveChat<S, I = unknown> {
144
192
  /** Eventos aplicados, em ordem (o `prompt()` e o `events()` usam). */
145
193
  tap(cb: (evs: Envelope[]) => void): () => void;
146
194
  get chatId(): string;
147
- /** Manda uma mensagem. Aparece na hora em `local.sending`; sai quando a inbox do log a mostra. */
195
+ /**
196
+ * Manda uma mensagem. Aparece na hora em `local.sending`; sai quando a inbox do log a mostra. Resolve quando
197
+ * gravada: pelo socket ou, se ele não confirmar em `sendFallbackMs`, pelo REST com o mesmo messageId (o servidor
198
+ * deduplica). `timeoutMs`: rejeita com `SendNotConfirmedError` ('timeout') se nada confirmou no prazo.
199
+ *
200
+ * O `timeoutMs` só desiste de ESPERAR: não cancela o envio pelo socket (o núcleo segue reenviando aquele frame
201
+ * até o ack ou o `close()`), então a mensagem ainda pode chegar à fila depois do timeout — e, chegando, a ordem
202
+ * relativa dela com os `send` seguintes não é garantida (o caminho do REST também não espera mais por ela).
203
+ * Reenviar com o mesmo `messageId` é seguro (o servidor deduplica).
204
+ */
148
205
  send(content: string | ContentPart[], opts?: {
149
206
  mode?: SendMode;
150
207
  attachments?: string[];
151
208
  messageId?: string;
209
+ timeoutMs?: number;
152
210
  }): Promise<SubmitResult & {
153
211
  messageId: string;
154
212
  }>;
213
+ /** Pede o caminho durável pra este send (e, pela ordem, pra todos os anteriores ainda esperando). */
214
+ private viaRest;
215
+ /**
216
+ * A corrente do REST, por chat: grava UM por vez, na ordem dos `send`. A cabeça é o send mais antigo ainda
217
+ * esperando; se algum depois dela pediu o REST, ela vai antes (a fila no servidor fica na ordem em que o usuário
218
+ * mandou). A cabeça confirmada (por qualquer caminho) libera a próxima. Falhou de vez: com o chat aberto a corrente
219
+ * para e tenta de novo com espera crescente, 5 s até 60 s (o socket também segue tentando); fechado, a cabeça e TODAS as de trás
220
+ * rejeitam — nenhuma pula a que não foi gravada.
221
+ */
222
+ private pumpRest;
223
+ private coreGaveUp;
224
+ private settleSend;
225
+ /** Gravado pelo REST: no `local.sending` passa de `sending` a `pending` (o núcleo não viu o ack). */
226
+ private markStored;
227
+ private overlayLocal;
155
228
  stop(): Promise<SubmitResult>;
156
229
  respond(requestId: string, answer: unknown): Promise<SubmitResult>;
157
230
  /** Sessão nova do provedor (era o recycle); `clearHistory` também esconde o histórico até aqui. */
@@ -189,6 +262,11 @@ export declare class LiveChat<S, I = unknown> {
189
262
  private wait;
190
263
  retry(): void;
191
264
  tokenRenewed(token: string): void;
265
+ /**
266
+ * Fecha. Nenhuma espera fica pendurada: o `send` não confirmado vai pelo REST (mesmo messageId) e resolve quando
267
+ * gravado; sem REST, rejeita com `SendNotConfirmedError` ('closed', com a mensagem). Os demais comandos
268
+ * rejeitam com `CommandAbortedError`.
269
+ */
192
270
  close(): void;
193
271
  readonly history: {
194
272
  /** Rola pra cima: página de itens fechados antes do primeiro da janela. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smoothcodes/agent-client",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "SmoothAgent SDK do usuário (token do usuário): REST gerada do contrato + a camada ao vivo (watch, events, prompt, lista de chats). Roda no navegador e no Node.",
6
6
  "license": "UNLICENSED",