@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.
Files changed (48) hide show
  1. package/README.md +77 -5
  2. package/catalog/agents.catalog.yaml +123 -0
  3. package/dist/catalog.d.ts +13 -0
  4. package/dist/catalog.js +39 -0
  5. package/dist/catalog.js.map +1 -0
  6. package/dist/cli.js +33 -8
  7. package/dist/cli.js.map +1 -1
  8. package/dist/connector.d.ts +13 -0
  9. package/dist/connector.js +208 -0
  10. package/dist/connector.js.map +1 -0
  11. package/dist/env.d.ts +56 -0
  12. package/dist/env.js +134 -0
  13. package/dist/env.js.map +1 -0
  14. package/dist/evals.js +1 -1
  15. package/dist/host.d.ts +111 -13
  16. package/dist/host.js +251 -43
  17. package/dist/host.js.map +1 -1
  18. package/dist/index.d.ts +16 -4
  19. package/dist/index.js +10 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/notifier.d.ts +1 -1
  22. package/dist/policy.js +12 -1
  23. package/dist/policy.js.map +1 -1
  24. package/dist/prompt.d.ts +8 -0
  25. package/dist/prompt.js +10 -3
  26. package/dist/prompt.js.map +1 -1
  27. package/dist/round.js +2 -1
  28. package/dist/round.js.map +1 -1
  29. package/dist/runtime-tools.d.ts +1 -1
  30. package/dist/runtime-tools.js +3 -3
  31. package/dist/runtime-tools.js.map +1 -1
  32. package/dist/signature.d.ts +34 -0
  33. package/dist/signature.js +87 -0
  34. package/dist/signature.js.map +1 -0
  35. package/dist/start.d.ts +31 -0
  36. package/dist/start.js +109 -0
  37. package/dist/start.js.map +1 -0
  38. package/dist/store/long-term.d.ts +63 -3
  39. package/dist/store/long-term.js +71 -11
  40. package/dist/store/long-term.js.map +1 -1
  41. package/dist/store/migrations.d.ts +1 -1
  42. package/dist/store/migrations.js +24 -0
  43. package/dist/store/migrations.js.map +1 -1
  44. package/dist/wiring.d.ts +11 -0
  45. package/dist/wiring.js +32 -0
  46. package/dist/wiring.js.map +1 -0
  47. package/package.json +13 -6
  48. package/proto/t4h/agents/v1/agents.proto +176 -0
package/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # @t4h/agent-runtime
2
2
 
3
3
  Runtime dos **colaboradores digitais** da Tech for Humans. Um agente é uma pasta
4
- (`agent.yaml`, `persona.md`, `skills/`, `evals/`); este pacote faz o resto:
4
+ (`agent.yaml`, `persona.md`, `skills/`, `evals/`); este pacote faz o resto, como o
5
+ `@t4h/mcp-provider-sdk` faz para os providers:
5
6
 
6
7
  - valida o `agent.yaml` (schema v1 em `schema/agent.schema.json` + regras entre campos);
7
- - caixa de entrada no Postgres: junta eventos por assunto, descarta repetido, ordena por prioridade;
8
+ - caixa de entrada no Postgres: junta eventos por assunto, descarta repetido (por fonte),
9
+ ordena por prioridade; evento novo acorda o agente, e o cron fica como rede de segurança;
8
10
  - rodadas com lock, teto de itens e de tempo, backoff e exceção para o DRI;
9
11
  - portão de política em toda chamada de ferramenta: nível por rotina (shadow, copilot,
10
12
  agent, autonomous), proibidas, motivo obrigatório, idempotência, janela e teto por hora;
@@ -12,13 +14,83 @@ Runtime dos **colaboradores digitais** da Tech for Humans. Um agente é uma past
12
14
  - memória curta (SQLite, uma execução) e longa (Postgres);
13
15
  - acesso aos sistemas só pelo Enterprise MCP Gateway, com a identidade própria do agente
14
16
  (client do Keycloak com service account);
15
- - cérebro: Claude Agent SDK isolado (sem ferramentas built-in, sem config do disco).
17
+ - cérebro: Claude Agent SDK isolado (sem ferramentas built-in, sem config do disco);
18
+ - o conector `agents_<círculo>` (proto, handlers e catálogo base, versionados juntos),
19
+ o ambiente validado no boot e o processo inteiro em `startAgents()`.
16
20
 
17
- O template de repositório de agentes e o agente de referência vivem em
18
- [`t4h-agents`](https://github.com/tech4humans-brasil/t4h-agents).
21
+ Cada círculo cria o seu repositório de agentes a partir de um template (ver
22
+ [Usar como template](../../web/src/content/agents/01-usar-como-template.md)). A documentação
23
+ completa está na seção **Agentes** do site do gateway.
19
24
 
20
25
  ```bash
21
26
  pnpm --filter @t4h/agent-runtime build
22
27
  pnpm --filter @t4h/agent-runtime test # sem modelo: ScriptedBrain + PGlite
23
28
  t4h-agent validate agents/ # valida os agent.yaml de um repositório
29
+ t4h-agent catalog financeiro --endpoint agentes-financeiro.example.com:443 > catalog.yaml
24
30
  ```
31
+
32
+ ## Entrypoint de um repositório de agentes
33
+
34
+ ```ts
35
+ import { startAgents } from '@t4h/agent-runtime';
36
+
37
+ await startAgents({ agentsDir: 'agents', pluginsDir: process.env.PLUGINS_DIR });
38
+ ```
39
+
40
+ `startAgents` carrega os agentes, valida o ambiente (`loadAgentEnv`), abre o banco e roda
41
+ as migrações, sobe o webhook de eventos, o cron e o conector gRPC (`createProviderServer`
42
+ do SDK, auditoria em JSON no stdout) e trata SIGINT/SIGTERM parando na ordem: entrada,
43
+ conector, conversas em andamento, banco. Opções: `wiring` (substitui o gateway por um stub
44
+ local; recusado em produção), `notifier`, `brain`. Fora de produção, sem as variáveis do
45
+ passaporte, sobe sem o conector (com aviso).
46
+
47
+ ## Conector `agents_<círculo>`
48
+
49
+ Contrato em `proto/t4h/agents/v1/agents.proto` (`AGENTS_SERVICE_FQN`, `agentsProtoPath()`),
50
+ handlers em `agentsConnectorHandlers(host)` e catálogo base em
51
+ `catalog/agents.catalog.yaml` (`agentsCatalog({ circle, endpoint })` ou
52
+ `t4h-agent catalog`). O slug segue `^agents_[a-z0-9_]+$`. Os grants são por tabela:
53
+
54
+ | Tabela | Tools | Quem recebe |
55
+ |---|---|---|
56
+ | `agente` | `agents_list`, `agent_ask`, `agent_answer` | quem pode ver e conversar com os agentes |
57
+ | `acao` | `agent_explain`, `action_correct` | quem audita e corrige |
58
+ | `proposta` | `agent_proposals`, `proposal_decide` | quem aprova (líder do time, DRI) |
59
+ | `controle` | `agent_block`, `routine_cap` | só Segurança e o DRI |
60
+
61
+ A conversa é assíncrona: o gateway corta a chamada ao provider em
62
+ `PROVIDER_CALL_TIMEOUT_MS` (30 s por padrão). `agent_ask` espera até `AGENTS_ASK_WAIT_MS`
63
+ (20 s) e, se a resposta não veio, devolve `running` com o `ask_id`; `agent_answer` busca
64
+ depois, de qualquer réplica, e só para quem perguntou.
65
+
66
+ ## Eventos
67
+
68
+ `POST /agents/:id/events` com `x-t4h-source: <fonte>` e
69
+ `x-t4h-signature: t=<unix s>,v1=<hex>`, onde `v1 = HMAC-SHA256(segredo da fonte, "<t>.<corpo cru>")`
70
+ (`signEvent()` monta o header). Tolerância de 300 s. O id do evento é gravado como
71
+ `<fonte>:<id>`. `POST /agents/:id/rounds` só existe fora de produção.
72
+
73
+ ## Ambiente
74
+
75
+ | Variável | Produção | Padrão / fora de produção |
76
+ |---|---|---|
77
+ | `DATABASE_URL` | `postgres://…` obrigatório | `pglite:<pasta>` ou em memória |
78
+ | `EVENTS_HMAC_SECRETS` | `fonte=segredo,…`, ao menos uma | sem: aceita sem assinatura |
79
+ | `EVENTS_PORT` / `EVENTS_WAKE_DEBOUNCE_MS` | | 8080 / 10000 |
80
+ | `GATEWAY_URL`, `KEYCLOAK_ISSUER` | obrigatórios | sem `GATEWAY_URL`: `wiring` local |
81
+ | `AGENT_<ID>_CLIENT_SECRET` | um por agente (hífen vira `_`) | |
82
+ | `GATEWAY_DEV_TOKEN` | proibido | token fixo no lugar do Keycloak |
83
+ | `ANTHROPIC_API_KEY` | obrigatório (ou `CLAUDE_CODE_USE_BEDROCK`/`_VERTEX`) | |
84
+ | `AGENTS_ASK_WAIT_MS` | | 20000, abaixo de `PROVIDER_CALL_TIMEOUT_MS` (30000) |
85
+ | `PORT`, `GATEWAY_JWKS_URL`, `GATEWAY_ISSUER`, `SERVICE_AUDIENCE`, mTLS | como em `loadProviderEnv()` | sem passaporte: sem conector |
86
+
87
+ ## Da 0.1 para a 0.2
88
+
89
+ - O conector (proto, handlers, catálogo) e o wiring do gateway saíram do template e
90
+ vieram para cá; o template deixa de depender de `@t4h/mcp-provider-sdk` e `@grpc/grpc-js`.
91
+ - `AskAgent` agora devolve `ask_id`/`status` e ganhou `GetAnswer`; `conversation_id` virou
92
+ `thread_id`. RPCs novas: `SetBlocked` e `CapRoutine` (tabela `controle`).
93
+ - `host.listen(port, secret)` virou `host.listen(port, { secrets, allowManualRounds, production })`;
94
+ `EVENTS_HMAC_SECRET` virou `EVENTS_HMAC_SECRETS` (por fonte, com timestamp) e
95
+ `verifySignature` saiu (use `verifyEventSignature`).
96
+ - Migração aditiva: tabela `agent_conversation` e coluna `agent_event.source`.
@@ -0,0 +1,123 @@
1
+ # Catálogo do conector de agentes de um círculo, gerado por
2
+ # t4h-agent catalog {{circle}} --endpoint {{endpoint}}
3
+ # a partir do catálogo base do @t4h/agent-runtime (versionado junto do proto). Não edite à
4
+ # mão: gere de novo com a versão do runtime que o círculo usa.
5
+ #
6
+ # Cada chave de inputSchema.properties é um campo do request no proto (snake_case). Grants
7
+ # (URN `agents_{{circle}}:<resourceTable>:<campo>`) seguem o fluxo de permissões da
8
+ # plataforma, nunca este arquivo:
9
+ # agente: agents, agent_id, ask_id, status, answer, error quem pode ver e conversar com os agentes
10
+ # acao: actions, action_id, status, routine_level quem audita e corrige
11
+ # proposta: proposals, action_id, status, result_json quem aprova (líder do time, DRI)
12
+ # controle: agent_id, blocked, routine, level só Segurança e o DRI
13
+ subservice: agents_{{circle}}
14
+ endpoints:
15
+ local: '{{local_endpoint}}'
16
+ production: '{{endpoint}}'
17
+ tools:
18
+ - name: agents_list
19
+ rpc: ListAgents
20
+ description: 'Lista os colaboradores digitais deste círculo: o que cada um faz, o nível de cada rotina agora (e por que desceu, se desceu), DRI, time e se está bloqueado.'
21
+ resourceTable: agente
22
+ readOnly: true
23
+ inputSchema: { type: object, properties: {} }
24
+
25
+ - name: agent_ask
26
+ rpc: AskAgent
27
+ description: 'Conversa com um agente. Ele só lê, com o acesso dele. Se a resposta vier com status "running", chame agent_answer com o ask_id em alguns segundos; se vier "error", agent_answer traz o motivo. Cada pessoa tem um teto de perguntas em andamento com o mesmo agente. Para pedir uma ação, abra a demanda pelo canal normal.'
28
+ resourceTable: agente
29
+ readOnly: false
30
+ inputSchema:
31
+ type: object
32
+ properties:
33
+ agent_id: { type: string }
34
+ message: { type: string }
35
+ thread_id: { type: string, description: 'Opcional: para continuar uma conversa, passe o ask_id da primeira pergunta (ou um id seu: até 64 letras, dígitos, "_" ou "-"). O agente recebe as últimas trocas desse fio feitas por você.' }
36
+ required: [agent_id, message]
37
+
38
+ - name: agent_answer
39
+ rpc: GetAnswer
40
+ description: 'Busca a resposta de uma pergunta feita com agent_ask. Enquanto vier "running", espere alguns segundos e chame de novo. Só quem perguntou lê a resposta.'
41
+ resourceTable: agente
42
+ readOnly: true
43
+ inputSchema:
44
+ type: object
45
+ properties:
46
+ ask_id: { type: string }
47
+ required: [ask_id]
48
+
49
+ - name: agent_explain
50
+ rpc: ExplainActions
51
+ description: 'Mostra o que um agente fez, em que modo (executou, propôs, só registrou, foi barrado) e com que motivo. Filtra por assunto (ex.: ticket:123).'
52
+ resourceTable: acao
53
+ readOnly: true
54
+ inputSchema:
55
+ type: object
56
+ properties:
57
+ agent_id: { type: string }
58
+ subject: { type: string }
59
+ limit: { type: integer, minimum: 1, maximum: 100 }
60
+ required: [agent_id]
61
+
62
+ - name: agent_proposals
63
+ rpc: ListProposals
64
+ description: 'Propostas de um agente esperando aprovação de uma pessoa (rotinas no nível Copiloto).'
65
+ resourceTable: proposta
66
+ readOnly: true
67
+ inputSchema:
68
+ type: object
69
+ properties:
70
+ agent_id: { type: string }
71
+ required: [agent_id]
72
+
73
+ - name: proposal_decide
74
+ rpc: DecideProposal
75
+ description: 'Aprova ou recusa uma proposta. Aprovada, a ação roda na hora com a identidade do agente e fica registrada com quem aprovou.'
76
+ resourceTable: proposta
77
+ readOnly: false
78
+ inputSchema:
79
+ type: object
80
+ properties:
81
+ action_id: { type: integer }
82
+ approve: { type: boolean }
83
+ note: { type: string }
84
+ required: [action_id, approve]
85
+
86
+ - name: action_correct
87
+ rpc: CorrectAction
88
+ description: 'Corrige uma ação que o agente executou: diga o que estava errado e o certo. Vira aprendizado e conta no disjuntor, que desce o nível da rotina se os erros passarem do limite.'
89
+ resourceTable: acao
90
+ readOnly: false
91
+ inputSchema:
92
+ type: object
93
+ properties:
94
+ action_id: { type: integer }
95
+ note: { type: string }
96
+ required: [action_id, note]
97
+
98
+ - name: agent_block
99
+ rpc: SetBlocked
100
+ description: 'Bloqueio de emergência: blocked=true para tudo o que o agente faz na hora, sem deploy (motivo obrigatório); blocked=false libera. Avisa o DRI e Segurança.'
101
+ resourceTable: controle
102
+ readOnly: false
103
+ inputSchema:
104
+ type: object
105
+ properties:
106
+ agent_id: { type: string }
107
+ blocked: { type: boolean }
108
+ reason: { type: string }
109
+ required: [agent_id, blocked]
110
+
111
+ - name: routine_cap
112
+ rpc: CapRoutine
113
+ description: 'Limita o nível de uma rotina do agente (shadow, copilot ou agent), com motivo. Com level vazio, tira o teto e volta a valer o nível do manifesto. Avisa o DRI e Segurança.'
114
+ resourceTable: controle
115
+ readOnly: false
116
+ inputSchema:
117
+ type: object
118
+ properties:
119
+ agent_id: { type: string }
120
+ routine: { type: string }
121
+ level: { type: string, enum: ['', shadow, copilot, agent] }
122
+ reason: { type: string }
123
+ required: [agent_id, routine, level]
@@ -0,0 +1,13 @@
1
+ /** `agents_<círculo>`: o schema do catálogo não aceita hífen no subservice. */
2
+ export declare const AGENTS_SLUG_PATTERN: RegExp;
3
+ export declare function agentsCatalogPath(): string;
4
+ export interface AgentsCatalogOptions {
5
+ /** Nome do círculo em minúsculas, com `_` no lugar de hífen: `financeiro`, `dados_bi`. */
6
+ circle: string;
7
+ /** Endpoint gRPC de produção do conector (`host:porta`). */
8
+ endpoint: string;
9
+ /** Endpoint para TARGET_ENV=local. Padrão: o mesmo de `endpoint`. */
10
+ localEndpoint?: string;
11
+ }
12
+ /** O catálogo de `agents_<círculo>` em YAML, pronto para o repositório de catálogos. */
13
+ export declare function agentsCatalog(o: AgentsCatalogOptions): string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Catálogo do conector `agents_<círculo>`: o base (`catalog/agents.catalog.yaml`) mora
3
+ * aqui, versionado junto do proto, para tool e RPC nunca se desencontrarem. O repositório
4
+ * de catálogos do gateway gera o de cada círculo com `t4h-agent catalog`, sem escrever à mão.
5
+ */
6
+ import { readFileSync } from 'node:fs';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { parse as parseYaml } from 'yaml';
9
+ /** `agents_<círculo>`: o schema do catálogo não aceita hífen no subservice. */
10
+ export const AGENTS_SLUG_PATTERN = /^agents_[a-z0-9_]+$/;
11
+ /** Mesmo padrão de endpoint do schema do catálogo (`host:porta`). */
12
+ const ENDPOINT_PATTERN = /^[a-zA-Z0-9.-]+:[0-9]{1,5}$/;
13
+ /** O subservice tem no máximo 50 caracteres (schema do catálogo). */
14
+ const MAX_SUBSERVICE = 50;
15
+ export function agentsCatalogPath() {
16
+ return fileURLToPath(new URL('../catalog/agents.catalog.yaml', import.meta.url));
17
+ }
18
+ /** O catálogo de `agents_<círculo>` em YAML, pronto para o repositório de catálogos. */
19
+ export function agentsCatalog(o) {
20
+ const slug = `agents_${o.circle}`;
21
+ if (!AGENTS_SLUG_PATTERN.test(slug) || slug.length > MAX_SUBSERVICE) {
22
+ throw new Error(`círculo "${o.circle}" inválido: use minúsculas, dígitos e _ (o slug ${slug} precisa casar ${AGENTS_SLUG_PATTERN} e ter até ${MAX_SUBSERVICE} caracteres)`);
23
+ }
24
+ const local = o.localEndpoint ?? o.endpoint;
25
+ for (const e of [o.endpoint, local]) {
26
+ if (!ENDPOINT_PATTERN.test(e))
27
+ throw new Error(`endpoint "${e}" inválido: use host:porta`);
28
+ }
29
+ const yaml = readFileSync(agentsCatalogPath(), 'utf8')
30
+ .replaceAll('{{circle}}', o.circle)
31
+ .replaceAll('{{local_endpoint}}', local)
32
+ .replaceAll('{{endpoint}}', o.endpoint);
33
+ // Falha aqui, e não no apply, se a substituição quebrar o YAML.
34
+ const parsed = parseYaml(yaml);
35
+ if (parsed.subservice !== slug)
36
+ throw new Error(`catálogo base gerou subservice "${parsed.subservice}", esperado ${slug}`);
37
+ return yaml;
38
+ }
39
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAEzD,qEAAqE;AACrE,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AAEvD,qEAAqE;AACrE,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,MAAM,UAAU,iBAAiB;IAC/B,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,gCAAgC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACnF,CAAC;AAWD,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,CAAuB;IACnD,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,mDAAmD,IAAI,kBAAkB,mBAAmB,cAAc,cAAc,cAAc,CAAC,CAAC;IAC9K,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,QAAQ,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,4BAA4B,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,IAAI,GAAG,YAAY,CAAC,iBAAiB,EAAE,EAAE,MAAM,CAAC;SACnD,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;SAClC,UAAU,CAAC,oBAAoB,EAAE,KAAK,CAAC;SACvC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,gEAAgE;IAChE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAA4B,CAAC;IAC1D,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,CAAC,UAAU,eAAe,IAAI,EAAE,CAAC,CAAC;IAC3H,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/cli.js CHANGED
@@ -1,14 +1,16 @@
1
1
  /**
2
- * t4h-agent validate <pasta agents/> valida todos os agent.yaml (schema + regras)
2
+ * t4h-agent validate [pasta agents/] valida todos os agent.yaml (schema + regras)
3
+ * t4h-agent catalog <círculo> --endpoint <host:porta> imprime o catálogo de agents_<círculo>
4
+ * [--local-endpoint <host:porta>]
3
5
  */
4
- import { loadAgents } from './manifest.js';
6
+ import { parseArgs } from 'node:util';
7
+ import { agentsCatalog } from './catalog.js';
5
8
  import { effectiveLevel } from './levels.js';
6
- const [cmd, dir = 'agents'] = process.argv.slice(2);
7
- if (cmd !== 'validate') {
8
- console.error('uso: t4h-agent validate [pasta]');
9
- process.exit(2);
10
- }
11
- try {
9
+ import { loadAgents } from './manifest.js';
10
+ const USAGE = `uso:
11
+ t4h-agent validate [pasta]
12
+ t4h-agent catalog <círculo> --endpoint <host:porta> [--local-endpoint <host:porta>]`;
13
+ function validate(dir = 'agents') {
12
14
  const agents = loadAgents(dir);
13
15
  for (const a of agents) {
14
16
  const m = a.manifest;
@@ -20,6 +22,29 @@ try {
20
22
  }
21
23
  console.info(`\n${agents.length} agente(s) válido(s).`);
22
24
  }
25
+ function catalog(args) {
26
+ const { values, positionals } = parseArgs({
27
+ args,
28
+ allowPositionals: true,
29
+ options: { endpoint: { type: 'string' }, 'local-endpoint': { type: 'string' } },
30
+ });
31
+ const [circle] = positionals;
32
+ if (!circle || !values.endpoint || positionals.length > 1)
33
+ throw new Error(USAGE);
34
+ // Só o YAML no stdout: dá para redirecionar direto para o catalog.yaml.
35
+ process.stdout.write(agentsCatalog({ circle, endpoint: values.endpoint, ...(values['local-endpoint'] ? { localEndpoint: values['local-endpoint'] } : {}) }));
36
+ }
37
+ const [cmd, ...rest] = process.argv.slice(2);
38
+ try {
39
+ if (cmd === 'validate')
40
+ validate(rest[0]);
41
+ else if (cmd === 'catalog')
42
+ catalog(rest);
43
+ else {
44
+ console.error(USAGE);
45
+ process.exit(2);
46
+ }
47
+ }
23
48
  catch (e) {
24
49
  console.error(e.message);
25
50
  process.exit(1);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEpD,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;IACvB,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,CAAC;IACH,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAChF,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnD,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACvE,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,uBAAuB,CAAC,CAAC;AAC1D,CAAC;AAAC,OAAO,CAAC,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,KAAK,GAAG;;sFAEwE,CAAC;AAEvF,SAAS,QAAQ,CAAC,GAAG,GAAG,QAAQ;IAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAChF,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnD,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACvE,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,uBAAuB,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,OAAO,CAAC,IAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KAChF,CAAC,CAAC;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;IAC7B,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAClF,wEAAwE;IACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CACvI,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAE7C,IAAI,CAAC;IACH,IAAI,GAAG,KAAK,UAAU;QAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;SACrC,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;SACrC,CAAC;QACJ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAAC,OAAO,CAAC,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type ProviderHandlers } from '@t4h/mcp-provider-sdk';
2
+ import { type AgentHost } from './host.js';
3
+ export declare const AGENTS_SERVICE_FQN = "t4h.agents.v1.AgentsService";
4
+ /** O proto do conector, publicado com o pacote (o gateway o puxa por reflection). */
5
+ export declare function agentsProtoPath(): string;
6
+ export interface AgentsConnectorOptions {
7
+ /** Quantas ações `ExplainActions` devolve sem `limit` (padrão 20) e no máximo (100). */
8
+ historyLimit?: {
9
+ default: number;
10
+ max: number;
11
+ };
12
+ }
13
+ export declare function agentsConnectorHandlers(host: AgentHost, opts?: AgentsConnectorOptions): ProviderHandlers;
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Handlers do conector de agentes do círculo (`proto/t4h/agents/v1/agents.proto`).
3
+ *
4
+ * Mesmo modelo de qualquer provider do gateway: o gateway autentica e autoriza (grants URN
5
+ * por papel, concedidos pela plataforma), manda a FieldMask e o passaporte; aqui só
6
+ * executamos e projetamos. `ctx.user` é auditoria e "quem pergunta": nunca autorização.
7
+ * Quem pode bloquear ou limitar um agente é decidido pelo grant da tabela `controle`.
8
+ */
9
+ import { status as grpcStatus } from '@grpc/grpc-js';
10
+ import { applyFieldMask, ProviderError } from '@t4h/mcp-provider-sdk';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { HostError } from './host.js';
13
+ import { effectiveLevel } from './levels.js';
14
+ import { CONVERSATION_ROUTINE } from './store/long-term.js';
15
+ export const AGENTS_SERVICE_FQN = 't4h.agents.v1.AgentsService';
16
+ /** O proto do conector, publicado com o pacote (o gateway o puxa por reflection). */
17
+ export function agentsProtoPath() {
18
+ return fileURLToPath(new URL('../proto/t4h/agents/v1/agents.proto', import.meta.url));
19
+ }
20
+ /** Tetos que uma pessoa pode pôr numa rotina pelo conector. */
21
+ const CAP_LEVELS = ['shadow', 'copilot', 'agent'];
22
+ function toAction(a) {
23
+ return {
24
+ id: String(a.id),
25
+ subject: a.subject ?? '',
26
+ routine: a.routine,
27
+ tool: a.tool,
28
+ mode: a.mode,
29
+ status: a.status,
30
+ level: a.level,
31
+ reason: a.reason ?? '',
32
+ input_json: JSON.stringify(a.input),
33
+ note: a.note ?? '',
34
+ created_at: new Date(a.created_at).toISOString(),
35
+ };
36
+ }
37
+ function agentOr404(host, id) {
38
+ if (!id || !host.agents.has(id))
39
+ throw new ProviderError(grpcStatus.NOT_FOUND, `agente "${id}" não existe neste conector`);
40
+ return host.agent(id);
41
+ }
42
+ function routineOr404(a, routine) {
43
+ const spec = a.manifest.routines[routine];
44
+ if (!routine || !spec)
45
+ throw new ProviderError(grpcStatus.NOT_FOUND, `${a.manifest.id} não tem a rotina "${routine}"`);
46
+ return spec;
47
+ }
48
+ /** O id de ação chega como texto (uint64 com `longs: String`). */
49
+ function actionId(raw) {
50
+ const id = Number(raw);
51
+ if (!Number.isSafeInteger(id) || id <= 0)
52
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, 'action_id inválido');
53
+ return id;
54
+ }
55
+ const HOST_STATUS = {
56
+ unavailable: grpcStatus.UNAVAILABLE,
57
+ resource_exhausted: grpcStatus.RESOURCE_EXHAUSTED,
58
+ invalid_argument: grpcStatus.INVALID_ARGUMENT,
59
+ };
60
+ /**
61
+ * Erro de regra do host (não existe, já decidida, bloqueado) vira FAILED_PRECONDITION;
62
+ * desligando, teto de conversas e pedido malformado têm status próprio.
63
+ */
64
+ function precondition(e) {
65
+ if (e instanceof ProviderError)
66
+ throw e;
67
+ if (e instanceof HostError)
68
+ throw new ProviderError(HOST_STATUS[e.code], e.message);
69
+ throw new ProviderError(grpcStatus.FAILED_PRECONDITION, e.message);
70
+ }
71
+ async function levelNow(host, a, routine) {
72
+ const spec = a.manifest.routines[routine];
73
+ if (!spec)
74
+ return '';
75
+ const caps = await host.store.levelCaps(a.manifest.id);
76
+ return effectiveLevel({ stage: a.manifest.stage, manifestLevel: spec.level, cap: caps.get(routine)?.cap ?? null });
77
+ }
78
+ export function agentsConnectorHandlers(host, opts = {}) {
79
+ const limits = opts.historyLimit ?? { default: 20, max: 100 };
80
+ // ── agente ──────────────────────────────────────────────────────────────────────
81
+ const listAgents = async (ctx) => {
82
+ const agents = await Promise.all([...host.agents.values()].map(async (a) => {
83
+ const m = a.manifest;
84
+ const caps = await host.store.levelCaps(m.id);
85
+ return {
86
+ id: m.id,
87
+ name: m.name,
88
+ description: m.description,
89
+ stage: m.stage,
90
+ dri: m.owner.dri,
91
+ team: `${m.team.area}/${m.team.team}`,
92
+ blocked: Boolean(await host.store.killSwitch(m.id)),
93
+ routines: Object.entries(m.routines).map(([name, r]) => ({
94
+ name,
95
+ does: r.does,
96
+ level: effectiveLevel({ stage: m.stage, manifestLevel: r.level, cap: caps.get(name)?.cap ?? null }),
97
+ target: r.target ?? r.level,
98
+ capped_reason: caps.get(name)?.reason ?? '',
99
+ })),
100
+ };
101
+ }));
102
+ return applyFieldMask({ agents }, ctx.fieldMaskPaths);
103
+ };
104
+ const askAgent = async (ctx) => {
105
+ const { agent_id, message, thread_id } = ctx.request;
106
+ agentOr404(host, agent_id);
107
+ if (!message?.trim())
108
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, 'message é obrigatório');
109
+ const r = await host.ask(agent_id, { email: ctx.user.email, kind: 'person' }, message, thread_id || undefined).catch(precondition);
110
+ // Sem o ask_id, quem perguntou não tem como buscar a resposta: vai sempre.
111
+ return applyFieldMask({ agent_id, ask_id: r.ask_id, status: r.status, answer: r.answer ?? '' }, ctx.fieldMaskPaths, {
112
+ alwaysInclude: ['agent_id', 'ask_id', 'status'],
113
+ });
114
+ };
115
+ const getAnswer = async (ctx) => {
116
+ const askId = ctx.request.ask_id?.trim();
117
+ if (!askId)
118
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, 'ask_id é obrigatório');
119
+ // Pergunta de outra pessoa responde igual a uma que não existe.
120
+ const row = await host.answer(askId, ctx.user.email);
121
+ if (!row)
122
+ throw new ProviderError(grpcStatus.NOT_FOUND, `pergunta "${askId}" não existe`);
123
+ return applyFieldMask({ ask_id: row.id, agent_id: row.agent_id, status: row.status, answer: row.answer ?? '', error: row.error ?? '' }, ctx.fieldMaskPaths, { alwaysInclude: ['ask_id', 'status'] });
124
+ };
125
+ // ── acao ────────────────────────────────────────────────────────────────────────
126
+ const explainActions = async (ctx) => {
127
+ agentOr404(host, ctx.request.agent_id);
128
+ // As conversas ficam de fora: guardam quem perguntou o quê, e só quem perguntou lê
129
+ // (pelo agent_answer).
130
+ const rows = await host.store.history(ctx.request.agent_id, {
131
+ excludeRoutine: CONVERSATION_ROUTINE,
132
+ ...(ctx.request.subject ? { subject: ctx.request.subject } : {}),
133
+ limit: Math.min(ctx.request.limit || limits.default, limits.max),
134
+ });
135
+ return applyFieldMask({ actions: rows.map(toAction) }, ctx.fieldMaskPaths);
136
+ };
137
+ const correctAction = async (ctx) => {
138
+ const id = actionId(ctx.request.action_id);
139
+ if (!ctx.request.note?.trim())
140
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, 'note é obrigatório: diga o que estava errado e o certo');
141
+ const row = await host.correctAction(id, ctx.user.email, ctx.request.note).catch(precondition);
142
+ const level = await levelNow(host, host.agent(row.agent_id), row.routine);
143
+ return applyFieldMask({ action_id: String(row.id), status: row.status, routine_level: level }, ctx.fieldMaskPaths, { alwaysInclude: ['action_id'] });
144
+ };
145
+ // ── proposta ────────────────────────────────────────────────────────────────────
146
+ const listProposals = async (ctx) => {
147
+ agentOr404(host, ctx.request.agent_id);
148
+ return applyFieldMask({ proposals: (await host.pendingProposals(ctx.request.agent_id)).map(toAction) }, ctx.fieldMaskPaths);
149
+ };
150
+ const decideProposal = async (ctx) => {
151
+ const id = actionId(ctx.request.action_id);
152
+ const row = await host
153
+ .decideProposal(id, ctx.request.approve ? 'approved' : 'rejected', ctx.user.email, ctx.request.note || undefined)
154
+ .catch(precondition);
155
+ return applyFieldMask({ action_id: String(row.id), status: row.status, result_json: JSON.stringify(row.result ?? null) }, ctx.fieldMaskPaths, {
156
+ alwaysInclude: ['action_id'],
157
+ });
158
+ };
159
+ // ── controle ────────────────────────────────────────────────────────────────────
160
+ const setBlocked = async (ctx) => {
161
+ const { agent_id, blocked } = ctx.request;
162
+ agentOr404(host, agent_id);
163
+ const reason = ctx.request.reason?.trim() ?? '';
164
+ if (blocked) {
165
+ if (!reason)
166
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, 'reason é obrigatório ao bloquear');
167
+ await host.kill(agent_id, ctx.user.email, reason);
168
+ }
169
+ else {
170
+ await host.unblock(agent_id, ctx.user.email, reason || undefined);
171
+ }
172
+ const now = Boolean(await host.store.killSwitch(agent_id));
173
+ return applyFieldMask({ agent_id, blocked: now }, ctx.fieldMaskPaths, { alwaysInclude: ['agent_id'] });
174
+ };
175
+ const capRoutine = async (ctx) => {
176
+ const { agent_id, routine } = ctx.request;
177
+ const a = agentOr404(host, agent_id);
178
+ routineOr404(a, routine);
179
+ const level = ctx.request.level?.trim() ?? '';
180
+ const reason = ctx.request.reason?.trim() ?? '';
181
+ if (!level) {
182
+ await host.restore(agent_id, routine, ctx.user.email, reason || undefined);
183
+ }
184
+ else {
185
+ if (!CAP_LEVELS.includes(level)) {
186
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, `level "${level}" inválido: use ${CAP_LEVELS.join(', ')} ou vazio para restaurar`);
187
+ }
188
+ if (!reason)
189
+ throw new ProviderError(grpcStatus.INVALID_ARGUMENT, 'reason é obrigatório ao limitar o nível');
190
+ await host.demote(agent_id, routine, level, ctx.user.email, reason);
191
+ }
192
+ return applyFieldMask({ agent_id, routine, level: await levelNow(host, a, routine) }, ctx.fieldMaskPaths, {
193
+ alwaysInclude: ['agent_id', 'routine'],
194
+ });
195
+ };
196
+ return {
197
+ ListAgents: listAgents,
198
+ AskAgent: askAgent,
199
+ GetAnswer: getAnswer,
200
+ ExplainActions: explainActions,
201
+ ListProposals: listProposals,
202
+ DecideProposal: decideProposal,
203
+ CorrectAction: correctAction,
204
+ SetBlocked: setBlocked,
205
+ CapRoutine: capRoutine,
206
+ };
207
+ }
208
+ //# sourceMappingURL=connector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
package/dist/env.d.ts ADDED
@@ -0,0 +1,56 @@
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 { type ProviderEnv } from '@t4h/mcp-provider-sdk';
12
+ import type { LoadedAgent } from './manifest.js';
13
+ export interface AgentEnv {
14
+ nodeEnv: string;
15
+ isProduction: boolean;
16
+ /** `postgres://…` em produção; fora dela também `pglite:<pasta>` ou ausente (em memória). */
17
+ databaseUrl?: string;
18
+ events: {
19
+ port: number;
20
+ /** Segredo HMAC por fonte. Vazio só fora de produção. */
21
+ secrets: Map<string, string>;
22
+ /** `POST /agents/:id/rounds`: fora de produção sim; em produção, nunca. */
23
+ allowManualRounds: boolean;
24
+ /** Espera entre o evento novo e a rodada que ele dispara (junta a rajada). */
25
+ wakeDebounceMs: number;
26
+ };
27
+ /** Ausente fora de produção sem `GATEWAY_URL`: quem sobe passa um `wiring` de stub. */
28
+ gateway?: {
29
+ url: string;
30
+ /** Só fora de produção: token fixo no lugar do client_credentials. */
31
+ devToken?: string;
32
+ keycloakIssuer?: string;
33
+ /** `AGENT_<ID>_CLIENT_SECRET` de cada agente, pelo id do agente. */
34
+ clientSecrets: Map<string, string>;
35
+ };
36
+ /** Ambiente do conector gRPC (`loadProviderEnv`). Ausente fora de produção sem passaporte configurado. */
37
+ connector?: ProviderEnv;
38
+ /**
39
+ * `PROVIDER_REQUIRE_CALL_BINDING=1`: o conector recusa passaporte sem o vínculo com a
40
+ * chamada (`requireCallBinding` do SDK de providers). Desligado por padrão, como no SDK.
41
+ */
42
+ requireCallBinding: boolean;
43
+ /** Quanto `agent_ask` espera a resposta antes de devolver `running`. */
44
+ askWaitMs: number;
45
+ debug: boolean;
46
+ }
47
+ /** Padrão do gateway para cortar a chamada ao provider (`PROVIDER_CALL_TIMEOUT_MS`). */
48
+ export declare const GATEWAY_CALL_TIMEOUT_MS = 30000;
49
+ export declare const DEFAULT_WAKE_DEBOUNCE_MS = 10000;
50
+ /** `AGENT_<ID>_CLIENT_SECRET`: hífen vira `_`, tudo em maiúsculas. */
51
+ export declare function agentSecretVar(agentId: string): string;
52
+ /**
53
+ * Lê e valida o ambiente para estes agentes. Junta todos os problemas numa mensagem só
54
+ * (quem sobe o processo corrige tudo de uma vez) e lança.
55
+ */
56
+ export declare function loadAgentEnv(agents: LoadedAgent[], env?: NodeJS.ProcessEnv): AgentEnv;