@t4h/agent-runtime 0.1.1 → 0.2.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/README.md +77 -5
- package/catalog/agents.catalog.yaml +123 -0
- package/dist/catalog.d.ts +13 -0
- package/dist/catalog.js +39 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.js +33 -8
- package/dist/cli.js.map +1 -1
- package/dist/connector.d.ts +13 -0
- package/dist/connector.js +208 -0
- package/dist/connector.js.map +1 -0
- package/dist/env.d.ts +56 -0
- package/dist/env.js +134 -0
- package/dist/env.js.map +1 -0
- package/dist/evals.js +1 -1
- package/dist/host.d.ts +111 -13
- package/dist/host.js +251 -43
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +16 -4
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/notifier.d.ts +1 -1
- package/dist/policy.js +12 -1
- package/dist/policy.js.map +1 -1
- package/dist/prompt.d.ts +8 -0
- package/dist/prompt.js +10 -3
- package/dist/prompt.js.map +1 -1
- package/dist/round.js +2 -1
- package/dist/round.js.map +1 -1
- package/dist/runtime-tools.d.ts +1 -1
- package/dist/runtime-tools.js +3 -3
- package/dist/runtime-tools.js.map +1 -1
- package/dist/signature.d.ts +34 -0
- package/dist/signature.js +87 -0
- package/dist/signature.js.map +1 -0
- package/dist/start.d.ts +31 -0
- package/dist/start.js +109 -0
- package/dist/start.js.map +1 -0
- package/dist/store/long-term.d.ts +63 -3
- package/dist/store/long-term.js +71 -11
- package/dist/store/long-term.js.map +1 -1
- package/dist/store/migrations.d.ts +1 -1
- package/dist/store/migrations.js +24 -0
- package/dist/store/migrations.js.map +1 -1
- package/dist/wiring.d.ts +11 -0
- package/dist/wiring.js +32 -0
- package/dist/wiring.js.map +1 -0
- package/package.json +13 -6
- package/proto/t4h/agents/v1/agents.proto +176 -0
package/dist/env.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambiente do processo dos agentes, num lugar só e fail-fast: espelha o `loadProviderEnv()`
|
|
3
|
+
* do SDK de providers (e o reusa para o conector). Combinação inválida falha no boot, não
|
|
4
|
+
* na primeira rodada.
|
|
5
|
+
*
|
|
6
|
+
* Em produção (`NODE_ENV=production`) não há atalho: Postgres de verdade, eventos
|
|
7
|
+
* assinados, identidade própria de cada agente no Keycloak, chave do modelo e o conector
|
|
8
|
+
* com mTLS. Fora de produção, o que faltar cai no modo local (PGlite, eventos sem
|
|
9
|
+
* assinatura, sem conector, `wiring` de stub no lugar do gateway).
|
|
10
|
+
*/
|
|
11
|
+
import { loadProviderEnv, optionalEnv } from '@t4h/mcp-provider-sdk';
|
|
12
|
+
import { parseEventSecrets } from './signature.js';
|
|
13
|
+
/** Padrão do gateway para cortar a chamada ao provider (`PROVIDER_CALL_TIMEOUT_MS`). */
|
|
14
|
+
export const GATEWAY_CALL_TIMEOUT_MS = 30_000;
|
|
15
|
+
export const DEFAULT_WAKE_DEBOUNCE_MS = 10_000;
|
|
16
|
+
/** `AGENT_<ID>_CLIENT_SECRET`: hífen vira `_`, tudo em maiúsculas. */
|
|
17
|
+
export function agentSecretVar(agentId) {
|
|
18
|
+
return `AGENT_${agentId.toUpperCase().replace(/-/g, '_')}_CLIENT_SECRET`;
|
|
19
|
+
}
|
|
20
|
+
function intEnv(name, fallback, env, problems, min = 0) {
|
|
21
|
+
const raw = optionalEnv(name, env);
|
|
22
|
+
if (raw === undefined)
|
|
23
|
+
return fallback;
|
|
24
|
+
const n = Number(raw);
|
|
25
|
+
if (!Number.isInteger(n) || n < min) {
|
|
26
|
+
problems.push(`${name}="${raw}" inválido: use um inteiro >= ${min}`);
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Lê e valida o ambiente para estes agentes. Junta todos os problemas numa mensagem só
|
|
33
|
+
* (quem sobe o processo corrige tudo de uma vez) e lança.
|
|
34
|
+
*/
|
|
35
|
+
export function loadAgentEnv(agents, env = process.env) {
|
|
36
|
+
const nodeEnv = env.NODE_ENV ?? 'development';
|
|
37
|
+
const isProduction = nodeEnv === 'production';
|
|
38
|
+
const problems = [];
|
|
39
|
+
// ── banco ───────────────────────────────────────────────────────────────────────
|
|
40
|
+
const databaseUrl = optionalEnv('DATABASE_URL', env);
|
|
41
|
+
const isPostgres = Boolean(databaseUrl && /^postgres(ql)?:\/\//.test(databaseUrl));
|
|
42
|
+
if (isProduction && !isPostgres)
|
|
43
|
+
problems.push('DATABASE_URL postgres://… é obrigatório em produção (PGlite só fora dela)');
|
|
44
|
+
else if (databaseUrl && !isPostgres && !databaseUrl.startsWith('pglite:'))
|
|
45
|
+
problems.push('DATABASE_URL: use postgres://… ou pglite:<pasta>');
|
|
46
|
+
// ── eventos ─────────────────────────────────────────────────────────────────────
|
|
47
|
+
let secrets = new Map();
|
|
48
|
+
try {
|
|
49
|
+
secrets = parseEventSecrets(optionalEnv('EVENTS_HMAC_SECRETS', env));
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
problems.push(e.message);
|
|
53
|
+
}
|
|
54
|
+
// O nome antigo (um segredo só) seria ignorado em silêncio: melhor parar.
|
|
55
|
+
if (optionalEnv('EVENTS_HMAC_SECRET', env))
|
|
56
|
+
problems.push('EVENTS_HMAC_SECRET não existe mais: use EVENTS_HMAC_SECRETS="fonte=segredo,…"');
|
|
57
|
+
if (isProduction && !secrets.size)
|
|
58
|
+
problems.push('EVENTS_HMAC_SECRETS com ao menos uma fonte é obrigatório em produção');
|
|
59
|
+
const eventsPort = intEnv('EVENTS_PORT', 8080, env, problems);
|
|
60
|
+
const wakeDebounceMs = intEnv('EVENTS_WAKE_DEBOUNCE_MS', DEFAULT_WAKE_DEBOUNCE_MS, env, problems);
|
|
61
|
+
// ── conversa ────────────────────────────────────────────────────────────────────
|
|
62
|
+
const gatewayTimeout = intEnv('PROVIDER_CALL_TIMEOUT_MS', GATEWAY_CALL_TIMEOUT_MS, env, problems, 1);
|
|
63
|
+
const askWaitMs = intEnv('AGENTS_ASK_WAIT_MS', 20_000, env, problems, 1);
|
|
64
|
+
if (askWaitMs >= gatewayTimeout) {
|
|
65
|
+
problems.push(`AGENTS_ASK_WAIT_MS (${askWaitMs}) tem de ficar abaixo do timeout do gateway para o provider (${gatewayTimeout} ms)`);
|
|
66
|
+
}
|
|
67
|
+
// ── gateway (identidade de cada agente) ─────────────────────────────────────────
|
|
68
|
+
const gatewayUrl = optionalEnv('GATEWAY_URL', env);
|
|
69
|
+
const devToken = optionalEnv('GATEWAY_DEV_TOKEN', env);
|
|
70
|
+
const keycloakIssuer = optionalEnv('KEYCLOAK_ISSUER', env);
|
|
71
|
+
const clientSecrets = new Map();
|
|
72
|
+
const missingSecrets = [];
|
|
73
|
+
for (const a of agents) {
|
|
74
|
+
const name = agentSecretVar(a.manifest.id);
|
|
75
|
+
const value = optionalEnv(name, env);
|
|
76
|
+
if (value)
|
|
77
|
+
clientSecrets.set(a.manifest.id, value);
|
|
78
|
+
else
|
|
79
|
+
missingSecrets.push(name);
|
|
80
|
+
}
|
|
81
|
+
let gateway;
|
|
82
|
+
if (isProduction) {
|
|
83
|
+
if (!gatewayUrl)
|
|
84
|
+
problems.push('GATEWAY_URL é obrigatório em produção');
|
|
85
|
+
if (devToken)
|
|
86
|
+
problems.push('GATEWAY_DEV_TOKEN é proibido em produção: cada agente usa a própria identidade');
|
|
87
|
+
if (!keycloakIssuer)
|
|
88
|
+
problems.push('KEYCLOAK_ISSUER é obrigatório em produção');
|
|
89
|
+
if (missingSecrets.length)
|
|
90
|
+
problems.push(`falta o segredo do client de cada agente: ${missingSecrets.join(', ')}`);
|
|
91
|
+
}
|
|
92
|
+
else if (gatewayUrl && !devToken && (!keycloakIssuer || missingSecrets.length)) {
|
|
93
|
+
problems.push(`com GATEWAY_URL, defina GATEWAY_DEV_TOKEN ou KEYCLOAK_ISSUER e ${missingSecrets.join(', ') || 'os segredos dos agentes'}`);
|
|
94
|
+
}
|
|
95
|
+
if (gatewayUrl) {
|
|
96
|
+
gateway = {
|
|
97
|
+
url: gatewayUrl,
|
|
98
|
+
...(devToken && !isProduction ? { devToken } : {}),
|
|
99
|
+
...(keycloakIssuer ? { keycloakIssuer } : {}),
|
|
100
|
+
clientSecrets,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ── modelo ──────────────────────────────────────────────────────────────────────
|
|
104
|
+
const hasModel = Boolean(optionalEnv('ANTHROPIC_API_KEY', env) || optionalEnv('CLAUDE_CODE_USE_BEDROCK', env) || optionalEnv('CLAUDE_CODE_USE_VERTEX', env));
|
|
105
|
+
if (isProduction && !hasModel)
|
|
106
|
+
problems.push('ANTHROPIC_API_KEY (ou CLAUDE_CODE_USE_BEDROCK / CLAUDE_CODE_USE_VERTEX) é obrigatório em produção');
|
|
107
|
+
// ── conector gRPC ───────────────────────────────────────────────────────────────
|
|
108
|
+
// Em produção, sempre (e o SDK já exige mTLS). Fora dela, só se o passaporte estiver
|
|
109
|
+
// configurado; configurado pela metade, falha como em produção.
|
|
110
|
+
let connector;
|
|
111
|
+
if (isProduction || optionalEnv('GATEWAY_JWKS_URL', env) || optionalEnv('SERVICE_JWT_PUBLIC_KEY', env)) {
|
|
112
|
+
try {
|
|
113
|
+
connector = loadProviderEnv(env);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
problems.push(`conector: ${e.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (problems.length) {
|
|
120
|
+
throw new Error(`ambiente inválido${isProduction ? ' (produção)' : ''}:\n - ${problems.join('\n - ')}`);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
nodeEnv,
|
|
124
|
+
isProduction,
|
|
125
|
+
...(databaseUrl ? { databaseUrl } : {}),
|
|
126
|
+
events: { port: eventsPort, secrets, allowManualRounds: !isProduction, wakeDebounceMs },
|
|
127
|
+
...(gateway ? { gateway } : {}),
|
|
128
|
+
...(connector ? { connector } : {}),
|
|
129
|
+
requireCallBinding: optionalEnv('PROVIDER_REQUIRE_CALL_BINDING', env) === '1',
|
|
130
|
+
askWaitMs,
|
|
131
|
+
debug: optionalEnv('AGENT_DEBUG', env) === '1',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
|
package/dist/evals.js
CHANGED
package/dist/host.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type IncomingEvent } from './inbox.js';
|
|
|
5
5
|
import type { LoadedAgent } from './manifest.js';
|
|
6
6
|
import { type Notifier } from './notifier.js';
|
|
7
7
|
import { type RoundReport } from './round.js';
|
|
8
|
-
import type
|
|
8
|
+
import { type ActionRow, type ConversationRow, type ConversationStatus, type LongTermStore } from './store/long-term.js';
|
|
9
9
|
/** Executa uma ação aprovada, sem o modelo. Em produção, o gateway com a identidade do agente. */
|
|
10
10
|
export interface ToolExecutor {
|
|
11
11
|
call(agent: LoadedAgent, qualifiedTool: string, input: Record<string, unknown>): Promise<{
|
|
@@ -14,6 +14,24 @@ export interface ToolExecutor {
|
|
|
14
14
|
}>;
|
|
15
15
|
}
|
|
16
16
|
export declare function gatewayExecutor(gateway: GatewayClient): ToolExecutor;
|
|
17
|
+
/**
|
|
18
|
+
* Recusa do host que não é regra do agente: o processo está desligando (`unavailable`),
|
|
19
|
+
* passou do teto de conversas simultâneas (`resource_exhausted`) ou o pedido veio
|
|
20
|
+
* malformado (`invalid_argument`). O conector traduz para o status gRPC equivalente.
|
|
21
|
+
*/
|
|
22
|
+
export declare class HostError extends Error {
|
|
23
|
+
readonly code: 'unavailable' | 'resource_exhausted' | 'invalid_argument';
|
|
24
|
+
constructor(code: 'unavailable' | 'resource_exhausted' | 'invalid_argument', message: string);
|
|
25
|
+
}
|
|
26
|
+
/** `thread_id` vem de quem chama: só letras, dígitos, `_` e `-` (um `ask_id` serve). */
|
|
27
|
+
export declare const THREAD_ID_PATTERN: RegExp;
|
|
28
|
+
/** Quantas trocas anteriores do mesmo fio entram no prompt da conversa. */
|
|
29
|
+
export declare const THREAD_HISTORY_LIMIT = 5;
|
|
30
|
+
/** Teto de conversas rodando ao mesmo tempo com um agente (somando as réplicas). */
|
|
31
|
+
export declare const DEFAULT_ASK_LIMITS: {
|
|
32
|
+
perAsker: number;
|
|
33
|
+
perAgent: number;
|
|
34
|
+
};
|
|
17
35
|
export interface AgentHostOptions {
|
|
18
36
|
agents: LoadedAgent[];
|
|
19
37
|
store: LongTermStore;
|
|
@@ -24,53 +42,133 @@ export interface AgentHostOptions {
|
|
|
24
42
|
notifier?: Notifier;
|
|
25
43
|
pluginsDir?: string;
|
|
26
44
|
now?: () => Date;
|
|
45
|
+
/**
|
|
46
|
+
* Quanto `ask()` espera a resposta antes de devolver `running` (padrão 20 s). Tem de
|
|
47
|
+
* ficar abaixo do timeout do gateway para a chamada ao provider (30 s por padrão).
|
|
48
|
+
*/
|
|
49
|
+
askWaitMs?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Teto de conversas `running` com um agente: por quem pergunta e no total. Acima dele,
|
|
52
|
+
* `ask()` recusa (`resource_exhausted`). Padrão: `DEFAULT_ASK_LIMITS`.
|
|
53
|
+
*/
|
|
54
|
+
askLimits?: {
|
|
55
|
+
perAsker: number;
|
|
56
|
+
perAgent: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Evento novo acorda o agente depois deste tempo, juntando os que chegarem no meio.
|
|
60
|
+
* Ausente: só o cron acorda (testes, demo).
|
|
61
|
+
*/
|
|
62
|
+
wakeDebounceMs?: number;
|
|
63
|
+
/** Relatório de cada rodada disparada pelo cron ou por evento. */
|
|
64
|
+
onRound?: (agentId: string, r: RoundReport) => void;
|
|
27
65
|
}
|
|
28
66
|
export interface Asker {
|
|
29
67
|
email: string;
|
|
30
68
|
kind: 'person' | 'agent';
|
|
31
69
|
}
|
|
70
|
+
export interface AskResult {
|
|
71
|
+
ask_id: string;
|
|
72
|
+
status: ConversationStatus;
|
|
73
|
+
answer?: string;
|
|
74
|
+
error?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface ListenOptions {
|
|
77
|
+
/** Segredo HMAC por fonte (`parseEventSecrets`). Vazio: só aceita fora de produção. */
|
|
78
|
+
secrets?: ReadonlyMap<string, string>;
|
|
79
|
+
/** `POST /agents/:id/rounds`. Padrão: fora de produção; em produção, nunca. */
|
|
80
|
+
allowManualRounds?: boolean;
|
|
81
|
+
/** Padrão: NODE_ENV=production. */
|
|
82
|
+
production?: boolean;
|
|
83
|
+
}
|
|
84
|
+
export declare const DEFAULT_ASK_WAIT_MS = 20000;
|
|
32
85
|
export declare class AgentHost {
|
|
33
86
|
private readonly o;
|
|
34
87
|
readonly agents: Map<string, LoadedAgent>;
|
|
35
88
|
private readonly skills;
|
|
36
89
|
private readonly crons;
|
|
90
|
+
private readonly wakeTimers;
|
|
91
|
+
/** Conversas e rodadas em andamento: o `stop()` espera um pouco por elas. */
|
|
92
|
+
private readonly inflight;
|
|
37
93
|
private readonly notifier;
|
|
94
|
+
private onRound;
|
|
38
95
|
private http?;
|
|
96
|
+
private stopped;
|
|
39
97
|
constructor(o: AgentHostOptions);
|
|
40
98
|
get store(): LongTermStore;
|
|
41
99
|
agent(id: string): LoadedAgent;
|
|
42
100
|
private skillsOf;
|
|
43
|
-
|
|
101
|
+
/** `source` é a fonte do webhook: o id do evento fica `<fonte>:<id>` (dedup por fonte). */
|
|
102
|
+
enqueue(agentId: string, ev: IncomingEvent, source?: string): Promise<{
|
|
44
103
|
itemId: number | null;
|
|
45
104
|
duplicate: boolean;
|
|
46
105
|
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Agenda uma rodada para logo depois de um evento novo. Os eventos que chegam enquanto
|
|
108
|
+
* a rodada está agendada entram nela (não reinicia a espera, para uma rajada não adiar a
|
|
109
|
+
* rodada para sempre). O lock continua garantindo uma rodada por vez; se outra réplica
|
|
110
|
+
* estiver rodando, tenta de novo depois. O cron segue como rede de segurança.
|
|
111
|
+
*/
|
|
112
|
+
private wake;
|
|
113
|
+
/** Acompanha o trabalho em andamento para o `stop()` esperar por ele antes do banco fechar. */
|
|
114
|
+
private track;
|
|
47
115
|
runRound(agentId: string, hops?: number): Promise<RoundReport>;
|
|
48
116
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
117
|
+
* Pergunta pelo conector: grava a conversa, roda em segundo plano e espera até
|
|
118
|
+
* `askWaitMs`. Se a resposta não vier a tempo, devolve `running`, e quem perguntou busca
|
|
119
|
+
* depois com `answer()`. O estado fica no Postgres: qualquer réplica responde.
|
|
120
|
+
*/
|
|
121
|
+
ask(agentId: string, asker: Asker, message: string, threadId?: string): Promise<AskResult>;
|
|
122
|
+
/**
|
|
123
|
+
* A resposta de um `ask()`. Só quem perguntou lê: para os outros, a conversa não existe.
|
|
124
|
+
* `running` há mais tempo que uma rodada inteira (mais um minuto) virou erro: a réplica
|
|
125
|
+
* que respondia caiu.
|
|
126
|
+
*/
|
|
127
|
+
answer(askId: string, askerEmail: string): Promise<ConversationRow | undefined>;
|
|
128
|
+
/**
|
|
129
|
+
* Conversa direta, à parte da fila. Síncrona: o conector usa `ask()`, que roda esta em
|
|
130
|
+
* segundo plano; aqui chega direto a conversa entre agentes (handoff). Só lê, com o
|
|
131
|
+
* acesso do próprio agente: ele é um colaborador virtual, e quem pode conversar com ele
|
|
132
|
+
* é decidido pelo grant do conector `agents_<círculo>`. `previous`: trocas anteriores do
|
|
133
|
+
* mesmo fio (o `ask()` só passa as de quem pergunta).
|
|
52
134
|
*/
|
|
53
|
-
converse(agentId: string, asker: Asker, message: string, conversationId: string, hops?: number
|
|
135
|
+
converse(agentId: string, asker: Asker, message: string, conversationId: string, hops?: number, previous?: Array<{
|
|
136
|
+
message: string;
|
|
137
|
+
answer: string | null;
|
|
138
|
+
}>): Promise<{
|
|
54
139
|
answer: string;
|
|
55
140
|
costUsd: number;
|
|
56
141
|
}>;
|
|
57
142
|
pendingProposals(agentId: string): Promise<ActionRow[]>;
|
|
58
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Uma pessoa aprova ou recusa a proposta. Aprovada, roda pelo gateway com a identidade do
|
|
145
|
+
* agente. Com o agente bloqueado, recusar vale; aprovar não (nada roda no nome dele).
|
|
146
|
+
*/
|
|
59
147
|
decideProposal(actionId: number, decision: 'approved' | 'rejected', by: string, note?: string): Promise<ActionRow>;
|
|
60
148
|
/** Uma pessoa corrige o que ele fez. Vira aprendizado no próximo prompt e conta no disjuntor. */
|
|
61
149
|
correctAction(actionId: number, by: string, note: string): Promise<ActionRow>;
|
|
150
|
+
private routineOf;
|
|
62
151
|
/** Incidente: a rotina desce na hora, por decisão de pessoa. */
|
|
63
152
|
demote(agentId: string, routine: string, to: 'shadow' | 'copilot' | 'agent', by: string, reason: string): Promise<void>;
|
|
153
|
+
/** Tira o teto: volta a valer o nível do manifesto (depois de tratar a causa). */
|
|
154
|
+
restore(agentId: string, routine: string, by: string, reason?: string): Promise<void>;
|
|
64
155
|
/** Bloqueio de emergência: para tudo na hora, sem deploy. */
|
|
65
156
|
kill(agentId: string, by: string, reason: string): Promise<void>;
|
|
157
|
+
/** Fim do bloqueio de emergência. Sem bloqueio, não faz nada (nem avisa). */
|
|
158
|
+
unblock(agentId: string, by: string, reason?: string): Promise<void>;
|
|
66
159
|
/** Liga o despertar de cada agente e a agenda das rotinas. */
|
|
67
160
|
startSchedules(onRound?: (agentId: string, r: RoundReport) => void): void;
|
|
68
161
|
/**
|
|
69
|
-
* Entrada de eventos por webhook: POST /agents/:id/events, assinado
|
|
70
|
-
* (`x-t4h-signature
|
|
162
|
+
* Entrada de eventos por webhook: POST /agents/:id/events, assinado por fonte
|
|
163
|
+
* (`x-t4h-source` + `x-t4h-signature`, ver signature.ts). POST /agents/:id/rounds acorda
|
|
164
|
+
* o agente na hora, só fora de produção. GET /healthz para o Container App.
|
|
165
|
+
*/
|
|
166
|
+
listen(port: number, opts?: ListenOptions): Promise<number>;
|
|
167
|
+
/**
|
|
168
|
+
* Para a entrada (webhook, cron, despertares, novas perguntas) e espera até `graceMs`
|
|
169
|
+
* pelas conversas e rodadas em andamento. A conversa que não terminar fica `running` e
|
|
170
|
+
* vira erro no próximo `answer()`; o item de uma rodada interrompida fica `working` e
|
|
171
|
+
* volta para a fila quando o prazo do lock vencer (`leaseBatch`).
|
|
71
172
|
*/
|
|
72
|
-
|
|
73
|
-
stop(): Promise<void>;
|
|
173
|
+
stop(graceMs?: number): Promise<void>;
|
|
74
174
|
}
|
|
75
|
-
/** Sem segredo configurado, só aceita fora de produção. */
|
|
76
|
-
export declare function verifySignature(raw: string, header: string | string[] | undefined, secret: string | undefined): boolean;
|