@t4h/agent-runtime 0.0.0-stage → 0.1.1

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 (61) hide show
  1. package/README.md +23 -2
  2. package/bin/t4h-agent.mjs +2 -0
  3. package/dist/brain.d.ts +48 -0
  4. package/dist/brain.js +99 -0
  5. package/dist/brain.js.map +1 -0
  6. package/dist/cli.d.ts +1 -0
  7. package/dist/cli.js +27 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/evals.d.ts +38 -0
  10. package/dist/evals.js +95 -0
  11. package/dist/evals.js.map +1 -0
  12. package/dist/gateway.d.ts +61 -0
  13. package/dist/gateway.js +93 -0
  14. package/dist/gateway.js.map +1 -0
  15. package/dist/host.d.ts +76 -0
  16. package/dist/host.js +267 -0
  17. package/dist/host.js.map +1 -0
  18. package/dist/inbox.d.ts +23 -0
  19. package/dist/inbox.js +29 -0
  20. package/dist/inbox.js.map +1 -0
  21. package/dist/index.d.ts +32 -0
  22. package/dist/index.js +19 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/levels.d.ts +48 -0
  25. package/dist/levels.js +73 -0
  26. package/dist/levels.js.map +1 -0
  27. package/dist/manifest.d.ts +111 -0
  28. package/dist/manifest.js +172 -0
  29. package/dist/manifest.js.map +1 -0
  30. package/dist/notifier.d.ts +26 -0
  31. package/dist/notifier.js +13 -0
  32. package/dist/notifier.js.map +1 -0
  33. package/dist/plugins.d.ts +8 -0
  34. package/dist/plugins.js +73 -0
  35. package/dist/plugins.js.map +1 -0
  36. package/dist/policy.d.ts +71 -0
  37. package/dist/policy.js +210 -0
  38. package/dist/policy.js.map +1 -0
  39. package/dist/prompt.d.ts +24 -0
  40. package/dist/prompt.js +84 -0
  41. package/dist/prompt.js.map +1 -0
  42. package/dist/round.d.ts +34 -0
  43. package/dist/round.js +108 -0
  44. package/dist/round.js.map +1 -0
  45. package/dist/runtime-tools.d.ts +75 -0
  46. package/dist/runtime-tools.js +114 -0
  47. package/dist/runtime-tools.js.map +1 -0
  48. package/dist/short-term.d.ts +16 -0
  49. package/dist/short-term.js +60 -0
  50. package/dist/short-term.js.map +1 -0
  51. package/dist/store/long-term.d.ts +136 -0
  52. package/dist/store/long-term.js +206 -0
  53. package/dist/store/long-term.js.map +1 -0
  54. package/dist/store/migrations.d.ts +5 -0
  55. package/dist/store/migrations.js +104 -0
  56. package/dist/store/migrations.js.map +1 -0
  57. package/dist/store/sql.d.ts +18 -0
  58. package/dist/store/sql.js +67 -0
  59. package/dist/store/sql.js.map +1 -0
  60. package/package.json +47 -3
  61. package/schema/agent.schema.json +236 -0
package/README.md CHANGED
@@ -1,3 +1,24 @@
1
- # Temporary Holding Version
1
+ # @t4h/agent-runtime
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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:
5
+
6
+ - 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
+ - rodadas com lock, teto de itens e de tempo, backoff e exceção para o DRI;
9
+ - portão de política em toda chamada de ferramenta: nível por rotina (shadow, copilot,
10
+ agent, autonomous), proibidas, motivo obrigatório, idempotência, janela e teto por hora;
11
+ - disjuntor que desce o nível da rotina quando erros e correções passam do limite;
12
+ - memória curta (SQLite, uma execução) e longa (Postgres);
13
+ - acesso aos sistemas só pelo Enterprise MCP Gateway, com a identidade própria do agente
14
+ (client do Keycloak com service account);
15
+ - cérebro: Claude Agent SDK isolado (sem ferramentas built-in, sem config do disco).
16
+
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).
19
+
20
+ ```bash
21
+ pnpm --filter @t4h/agent-runtime build
22
+ pnpm --filter @t4h/agent-runtime test # sem modelo: ScriptedBrain + PGlite
23
+ t4h-agent validate agents/ # valida os agent.yaml de um repositório
24
+ ```
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ await import('../dist/cli.js');
@@ -0,0 +1,48 @@
1
+ /**
2
+ * O "cérebro" de uma execução: recebe o prompt, as ferramentas e o portão de política, e
3
+ * trabalha até fechar o item ou responder a conversa.
4
+ *
5
+ * `ClaudeBrain` usa o Claude Agent SDK (o harness do Claude Code como biblioteca), isolado:
6
+ * - sem ferramentas built-in (nada de Bash, Read, Edit): só MCP do gateway, as do runtime
7
+ * e o carregamento de skill;
8
+ * - sem configuração do disco (`settingSources: []`) e sem MCP de plugin
9
+ * (`skipMcpDiscovery`, `strictMcpConfig`): quem decide conexões é o runtime;
10
+ * - toda chamada passa pelo PolicyGate, no hook PreToolUse e de novo no canUseTool;
11
+ * - sem transcrição em disco (`persistSession: false`): o histórico que vale é o Postgres.
12
+ *
13
+ * Os testes usam um Brain roteirizado que chama o mesmo portão, sem modelo.
14
+ */
15
+ import { type McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
16
+ import type { LoadedAgent } from './manifest.js';
17
+ import type { PolicyGate } from './policy.js';
18
+ import type { ResolvedSkills } from './plugins.js';
19
+ import type { RuntimeTools } from './runtime-tools.js';
20
+ export interface BrainTask {
21
+ agent: LoadedAgent;
22
+ systemPrompt: string;
23
+ prompt: string;
24
+ gate: PolicyGate;
25
+ tools: RuntimeTools;
26
+ /** Conectores do gateway (ou stubs locais), sem o `runtime`. */
27
+ mcpServers: Record<string, McpServerConfig>;
28
+ skills: ResolvedSkills;
29
+ maxTurns: number;
30
+ budgetUsd?: number;
31
+ signal: AbortSignal;
32
+ }
33
+ export interface BrainResult {
34
+ text: string;
35
+ costUsd: number;
36
+ turns: number;
37
+ error?: string;
38
+ }
39
+ export interface Brain {
40
+ run(task: BrainTask): Promise<BrainResult>;
41
+ }
42
+ export declare class ClaudeBrain implements Brain {
43
+ private readonly opts;
44
+ constructor(opts?: {
45
+ debug?: boolean;
46
+ });
47
+ run(task: BrainTask): Promise<BrainResult>;
48
+ }
package/dist/brain.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * O "cérebro" de uma execução: recebe o prompt, as ferramentas e o portão de política, e
3
+ * trabalha até fechar o item ou responder a conversa.
4
+ *
5
+ * `ClaudeBrain` usa o Claude Agent SDK (o harness do Claude Code como biblioteca), isolado:
6
+ * - sem ferramentas built-in (nada de Bash, Read, Edit): só MCP do gateway, as do runtime
7
+ * e o carregamento de skill;
8
+ * - sem configuração do disco (`settingSources: []`) e sem MCP de plugin
9
+ * (`skipMcpDiscovery`, `strictMcpConfig`): quem decide conexões é o runtime;
10
+ * - toda chamada passa pelo PolicyGate, no hook PreToolUse e de novo no canUseTool;
11
+ * - sem transcrição em disco (`persistSession: false`): o histórico que vale é o Postgres.
12
+ *
13
+ * Os testes usam um Brain roteirizado que chama o mesmo portão, sem modelo.
14
+ */
15
+ import { query } from '@anthropic-ai/claude-agent-sdk';
16
+ function isErrorResponse(r) {
17
+ return typeof r === 'object' && r !== null && r.isError === true;
18
+ }
19
+ export class ClaudeBrain {
20
+ opts;
21
+ constructor(opts = {}) {
22
+ this.opts = opts;
23
+ }
24
+ async run(task) {
25
+ const { gate } = task;
26
+ const abortController = new AbortController();
27
+ task.signal.addEventListener('abort', () => abortController.abort(), { once: true });
28
+ const preToolUse = async (input, toolUseID) => {
29
+ if (input.hook_event_name !== 'PreToolUse')
30
+ return {};
31
+ const id = toolUseID ?? input.tool_use_id;
32
+ const d = await gate.check(input.tool_name, (input.tool_input ?? {}), id);
33
+ return {
34
+ hookSpecificOutput: {
35
+ hookEventName: 'PreToolUse',
36
+ permissionDecision: d.allow ? 'allow' : 'deny',
37
+ ...(d.allow ? {} : { permissionDecisionReason: d.message }),
38
+ },
39
+ };
40
+ };
41
+ const postToolUse = async (input) => {
42
+ if (input.hook_event_name === 'PostToolUse') {
43
+ await gate.after(input.tool_use_id, !isErrorResponse(input.tool_response), input.tool_response);
44
+ }
45
+ else if (input.hook_event_name === 'PostToolUseFailure') {
46
+ await gate.after(input.tool_use_id, false, { error: input.error });
47
+ }
48
+ return {};
49
+ };
50
+ // Segunda trava: se algo chegar à checagem de permissão, vale a decisão do portão.
51
+ const canUseTool = async (toolName, input, o) => {
52
+ const d = gate.decided(o.toolUseID) ?? (await gate.check(toolName, input, o.toolUseID));
53
+ return d.allow ? { behavior: 'allow', updatedInput: input } : { behavior: 'deny', message: d.message };
54
+ };
55
+ const m = task.agent.manifest;
56
+ let text = '';
57
+ let costUsd = 0;
58
+ let turns = 0;
59
+ let error;
60
+ const q = query({
61
+ prompt: task.prompt,
62
+ options: {
63
+ model: m.model,
64
+ effort: m.effort,
65
+ systemPrompt: task.systemPrompt,
66
+ tools: task.skills.skills.length ? ['Skill'] : [],
67
+ ...(task.skills.skills.length ? { skills: task.skills.skills } : {}),
68
+ plugins: task.skills.pluginPaths.map((path) => ({ type: 'local', path, skipMcpDiscovery: true })),
69
+ mcpServers: { ...task.mcpServers, runtime: task.tools.sdkServer() },
70
+ strictMcpConfig: true,
71
+ settingSources: [],
72
+ permissionMode: 'default',
73
+ canUseTool,
74
+ hooks: {
75
+ PreToolUse: [{ hooks: [preToolUse] }],
76
+ PostToolUse: [{ hooks: [postToolUse] }],
77
+ PostToolUseFailure: [{ hooks: [postToolUse] }],
78
+ },
79
+ maxTurns: task.maxTurns,
80
+ ...(task.budgetUsd ? { maxBudgetUsd: task.budgetUsd } : {}),
81
+ persistSession: false,
82
+ abortController,
83
+ ...(this.opts.debug ? { stderr: (s) => process.stderr.write(s) } : {}),
84
+ },
85
+ });
86
+ for await (const msg of q) {
87
+ if (msg.type === 'result') {
88
+ costUsd = msg.total_cost_usd;
89
+ turns = msg.num_turns;
90
+ if (msg.subtype === 'success')
91
+ text = msg.result;
92
+ else
93
+ error = `${msg.subtype}`;
94
+ }
95
+ }
96
+ return { text, costUsd, turns, ...(error ? { error } : {}) };
97
+ }
98
+ }
99
+ //# sourceMappingURL=brain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain.js","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,EAAkE,MAAM,gCAAgC,CAAC;AA+BvH,SAAS,eAAe,CAAC,CAAU;IACjC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAA2B,CAAC,OAAO,KAAK,IAAI,CAAC;AAC9F,CAAC;AAED,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,OAA4B,EAAE;QAA9B,SAAI,GAAJ,IAAI,CAA0B;IAAG,CAAC;IAE/D,KAAK,CAAC,GAAG,CAAC,IAAe;QACvB,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QACtB,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAErF,MAAM,UAAU,GAAiB,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC1D,IAAI,KAAK,CAAC,eAAe,KAAK,YAAY;gBAAE,OAAO,EAAE,CAAC;YACtD,MAAM,EAAE,GAAG,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC;YAC1C,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAA4B,EAAE,EAAE,CAAC,CAAC;YACrG,OAAO;gBACL,kBAAkB,EAAE;oBAClB,aAAa,EAAE,YAAY;oBAC3B,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;oBAC9C,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,wBAAwB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;iBAC5D;aACF,CAAC;QACJ,CAAC,CAAC;QACF,MAAM,WAAW,GAAiB,KAAK,EAAE,KAAK,EAAE,EAAE;YAChD,IAAI,KAAK,CAAC,eAAe,KAAK,aAAa,EAAE,CAAC;gBAC5C,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;YAClG,CAAC;iBAAM,IAAI,KAAK,CAAC,eAAe,KAAK,oBAAoB,EAAE,CAAC;gBAC1D,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACrE,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,mFAAmF;QACnF,MAAM,UAAU,GAAG,KAAK,EACtB,QAAgB,EAChB,KAA8B,EAC9B,CAAwB,EACG,EAAE;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YACxF,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QACzG,CAAC,CAAC;QAEF,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC9B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,KAAyB,CAAC;QAE9B,MAAM,CAAC,GAAG,KAAK,CAAC;YACd,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE;gBACP,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;gBACjD,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC1G,UAAU,EAAE,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE;gBACnE,eAAe,EAAE,IAAI;gBACrB,cAAc,EAAE,EAAE;gBAClB,cAAc,EAAE,SAAS;gBACzB,UAAU;gBACV,KAAK,EAAE;oBACL,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;oBACrC,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;oBACvC,kBAAkB,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;iBAC/C;gBACD,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3D,cAAc,EAAE,KAAK;gBACrB,eAAe;gBACf,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC/E;SACF,CAAC,CAAC;QAEH,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;YAC1B,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1B,OAAO,GAAG,GAAG,CAAC,cAAc,CAAC;gBAC7B,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC;gBACtB,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;oBAAE,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC;;oBAC5C,KAAK,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;YAChC,CAAC;QACH,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC/D,CAAC;CACF"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * t4h-agent validate <pasta agents/> valida todos os agent.yaml (schema + regras)
3
+ */
4
+ import { loadAgents } from './manifest.js';
5
+ 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 {
12
+ const agents = loadAgents(dir);
13
+ for (const a of agents) {
14
+ const m = a.manifest;
15
+ console.info(`✓ ${m.id} · ${m.name} · estágio ${m.stage} · DRI ${m.owner.dri}`);
16
+ for (const [name, r] of Object.entries(m.routines)) {
17
+ const now = effectiveLevel({ stage: m.stage, manifestLevel: r.level });
18
+ console.info(` ${name.padEnd(18)} ${now.padEnd(10)}${r.target ? ` → ${r.target}` : ''} ${r.actions.join(', ')}`);
19
+ }
20
+ }
21
+ console.info(`\n${agents.length} agente(s) válido(s).`);
22
+ }
23
+ catch (e) {
24
+ console.error(e.message);
25
+ process.exit(1);
26
+ }
27
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +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"}
@@ -0,0 +1,38 @@
1
+ import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
2
+ import type { Brain } from './brain.js';
3
+ import { type ToolExecutor } from './host.js';
4
+ import type { IncomingEvent } from './inbox.js';
5
+ import type { LoadedAgent } from './manifest.js';
6
+ export interface EvalCase {
7
+ name: string;
8
+ events: IncomingEvent[];
9
+ expect: {
10
+ calls?: Array<string | {
11
+ tool: string;
12
+ input?: Record<string, unknown>;
13
+ }>;
14
+ notCalls?: string[];
15
+ escalates?: boolean;
16
+ finish?: 'done' | 'waiting';
17
+ };
18
+ }
19
+ export interface EvalResult {
20
+ name: string;
21
+ passed: boolean;
22
+ problems: string[];
23
+ costUsd: number;
24
+ }
25
+ export declare function loadEvalCases(agent: LoadedAgent): EvalCase[];
26
+ /** O valor esperado está contido no recebido (objetos por chave, o resto por igualdade). */
27
+ export declare function subsetMatch(expected: unknown, actual: unknown): boolean;
28
+ export declare function runEvalCase(agent: LoadedAgent, c: EvalCase, deps: {
29
+ brain: Brain;
30
+ servers: (a: LoadedAgent) => Promise<Record<string, McpServerConfig>>;
31
+ executor: ToolExecutor;
32
+ pluginsDir?: string;
33
+ }): Promise<EvalResult>;
34
+ export declare function runEvals(agent: LoadedAgent, deps: Parameters<typeof runEvalCase>[2], filter?: (c: EvalCase) => boolean): Promise<{
35
+ results: EvalResult[];
36
+ passRate: number;
37
+ costUsd: number;
38
+ }>;
package/dist/evals.js ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Evals do agente: cada caso entra como eventos numa caixa de entrada limpa, roda uma
3
+ * rodada de verdade (modelo, portão, rotinas) e confere o que ele tentou fazer.
4
+ *
5
+ * "Tentou" vale em qualquer nível: executada, proposta ou registrada em shadow. Assim o
6
+ * mesmo caso mede acerto no shadow e continua valendo depois que a rotina sobe.
7
+ *
8
+ * Formato (agents/<id>/evals/*.yaml):
9
+ *
10
+ * - name: chamado de custo vai para FinOps
11
+ * events:
12
+ * - { type: exemplo.item.opened, subject: "item:42", payload: { titulo: "..." } }
13
+ * expect:
14
+ * calls: [{ tool: exemplo.item_route, input: { team: financeiro } }]
15
+ * notCalls: [exemplo.item_close]
16
+ * escalates: false
17
+ * finish: done
18
+ */
19
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
20
+ import { join } from 'node:path';
21
+ import { parse as parseYaml } from 'yaml';
22
+ import { AgentHost } from './host.js';
23
+ import { MemoryNotifier } from './notifier.js';
24
+ import { LongTermStore } from './store/long-term.js';
25
+ import { pgliteSql } from './store/sql.js';
26
+ export function loadEvalCases(agent) {
27
+ const dir = join(agent.dir, 'evals');
28
+ if (!existsSync(dir))
29
+ return [];
30
+ return readdirSync(dir)
31
+ .filter((f) => /\.ya?ml$/.test(f))
32
+ .sort()
33
+ .flatMap((f) => {
34
+ const doc = parseYaml(readFileSync(join(dir, f), 'utf8'));
35
+ return Array.isArray(doc) ? doc : doc.cases;
36
+ });
37
+ }
38
+ /** O valor esperado está contido no recebido (objetos por chave, o resto por igualdade). */
39
+ export function subsetMatch(expected, actual) {
40
+ if (expected === null || typeof expected !== 'object') {
41
+ if (typeof expected === 'string' && typeof actual === 'string')
42
+ return actual.toLowerCase().includes(expected.toLowerCase());
43
+ return expected === actual;
44
+ }
45
+ if (typeof actual !== 'object' || actual === null)
46
+ return false;
47
+ return Object.entries(expected).every(([k, v]) => subsetMatch(v, actual[k]));
48
+ }
49
+ export async function runEvalCase(agent, c, deps) {
50
+ const store = new LongTermStore(await pgliteSql());
51
+ await store.migrate();
52
+ const notifier = new MemoryNotifier();
53
+ const host = new AgentHost({ agents: [agent], store, notifier, ...deps });
54
+ const problems = [];
55
+ try {
56
+ for (const ev of c.events)
57
+ await host.enqueue(agent.manifest.id, ev);
58
+ const report = await host.runRound(agent.manifest.id);
59
+ const attempted = (await store.history(agent.manifest.id, { limit: 200 })).filter((a) => a.mode !== 'denied');
60
+ for (const exp of c.expect.calls ?? []) {
61
+ const e = typeof exp === 'string' ? { tool: exp } : exp;
62
+ const hit = attempted.find((a) => a.tool === e.tool && (!e.input || subsetMatch(e.input, a.input)));
63
+ if (!hit) {
64
+ const same = attempted.filter((a) => a.tool === e.tool).map((a) => JSON.stringify(a.input));
65
+ problems.push(`esperava ${e.tool}${e.input ? ` com ${JSON.stringify(e.input)}` : ''}${same.length ? `; veio ${same.join(' | ')}` : '; não chamou'}`);
66
+ }
67
+ }
68
+ for (const t of c.expect.notCalls ?? []) {
69
+ if (attempted.some((a) => a.tool === t))
70
+ problems.push(`não devia chamar ${t}`);
71
+ }
72
+ if (c.expect.escalates !== undefined) {
73
+ const escalated = attempted.some((a) => a.tool === 'runtime.escalate');
74
+ if (escalated !== c.expect.escalates)
75
+ problems.push(c.expect.escalates ? 'esperava escalar' : 'não devia escalar');
76
+ }
77
+ if (c.expect.finish) {
78
+ const results = report.items.map((i) => i.result);
79
+ if (!results.includes(c.expect.finish))
80
+ problems.push(`esperava fechar como ${c.expect.finish}; veio ${results.join(', ') || 'nada'}`);
81
+ }
82
+ return { name: c.name, passed: problems.length === 0, problems, costUsd: report.costUsd };
83
+ }
84
+ finally {
85
+ await store.close();
86
+ }
87
+ }
88
+ export async function runEvals(agent, deps, filter) {
89
+ const results = [];
90
+ for (const c of loadEvalCases(agent).filter(filter ?? (() => true)))
91
+ results.push(await runEvalCase(agent, c, deps));
92
+ const passRate = results.length ? results.filter((r) => r.passed).length / results.length : 0;
93
+ return { results, passRate, costUsd: results.reduce((s, r) => s + r.costUsd, 0) };
94
+ }
95
+ //# sourceMappingURL=evals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evals.js","sourceRoot":"","sources":["../src/evals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * O agente usa os sistemas da T4H só pelo MCP Gateway, com a própria identidade.
3
+ *
4
+ * O agente é um colaborador virtual: identidade e permissões próprias. Para os sistemas, é
5
+ * mais um e-mail: um client do Keycloak com service account (client_credentials) cujo
6
+ * usuário tem o e-mail de agente, criado por Segurança. O gateway já decide tudo pelo
7
+ * e-mail e pelos papéis do token; os grants do agente são pedidos e liberados como os de
8
+ * uma pessoa. Nada disso é configurado aqui.
9
+ */
10
+ import type { McpHttpServerConfig } from '@anthropic-ai/claude-agent-sdk';
11
+ export interface TokenSource {
12
+ token(): Promise<string>;
13
+ }
14
+ /** Fallback de desenvolvimento: o gateway em modo dev aceita HS256 com DEV_JWT_SECRET. */
15
+ export declare class StaticTokenSource implements TokenSource {
16
+ private readonly value;
17
+ constructor(value: string);
18
+ token(): Promise<string>;
19
+ }
20
+ /** Token curto do Keycloak, renovado um minuto antes de vencer. */
21
+ export declare class ClientCredentialsTokenSource implements TokenSource {
22
+ private readonly cfg;
23
+ private readonly fetchImpl;
24
+ private cached?;
25
+ constructor(cfg: {
26
+ issuer: string;
27
+ clientId: string;
28
+ clientSecret: string;
29
+ scope?: string;
30
+ }, fetchImpl?: typeof fetch);
31
+ token(): Promise<string>;
32
+ }
33
+ export interface GatewayTool {
34
+ name: string;
35
+ description?: string;
36
+ inputSchema?: unknown;
37
+ }
38
+ export declare class GatewayError extends Error {
39
+ readonly code?: number | undefined;
40
+ constructor(message: string, code?: number | undefined);
41
+ }
42
+ /** Cliente JSON-RPC do `POST /mcp/:subservice` (o gateway é stateless, sem sessão). */
43
+ export declare class GatewayClient {
44
+ private readonly cfg;
45
+ private readonly fetchImpl;
46
+ private seq;
47
+ constructor(cfg: {
48
+ url: string;
49
+ tokens: TokenSource;
50
+ }, fetchImpl?: typeof fetch);
51
+ endpoint(subservice: string): string;
52
+ /** Config de MCP remoto para o Claude Agent SDK, um por conector, com o token do agente. */
53
+ mcpServers(subservices: string[], tokens?: TokenSource): Promise<Record<string, McpHttpServerConfig>>;
54
+ rpc<T>(subservice: string, method: string, params: unknown, tokens?: TokenSource): Promise<T>;
55
+ listTools(subservice: string, tokens?: TokenSource): Promise<GatewayTool[]>;
56
+ /** Executa uma ação já aprovada (proposta do copilot), sem passar pelo modelo. */
57
+ callTool(subservice: string, name: string, args: Record<string, unknown>): Promise<{
58
+ ok: boolean;
59
+ content: unknown;
60
+ }>;
61
+ }
@@ -0,0 +1,93 @@
1
+ /** Fallback de desenvolvimento: o gateway em modo dev aceita HS256 com DEV_JWT_SECRET. */
2
+ export class StaticTokenSource {
3
+ value;
4
+ constructor(value) {
5
+ this.value = value;
6
+ }
7
+ async token() {
8
+ return this.value;
9
+ }
10
+ }
11
+ /** Token curto do Keycloak, renovado um minuto antes de vencer. */
12
+ export class ClientCredentialsTokenSource {
13
+ cfg;
14
+ fetchImpl;
15
+ cached;
16
+ constructor(cfg, fetchImpl = fetch) {
17
+ this.cfg = cfg;
18
+ this.fetchImpl = fetchImpl;
19
+ }
20
+ async token() {
21
+ const now = Date.now();
22
+ if (this.cached && this.cached.exp - 60_000 > now)
23
+ return this.cached.value;
24
+ const body = new URLSearchParams({
25
+ grant_type: 'client_credentials',
26
+ client_id: this.cfg.clientId,
27
+ client_secret: this.cfg.clientSecret,
28
+ ...(this.cfg.scope ? { scope: this.cfg.scope } : {}),
29
+ });
30
+ const res = await this.fetchImpl(`${this.cfg.issuer.replace(/\/$/, '')}/protocol/openid-connect/token`, {
31
+ method: 'POST',
32
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
33
+ body,
34
+ });
35
+ if (!res.ok)
36
+ throw new Error(`Keycloak recusou o client_credentials de ${this.cfg.clientId}: HTTP ${res.status}`);
37
+ const json = (await res.json());
38
+ this.cached = { value: json.access_token, exp: now + json.expires_in * 1000 };
39
+ return json.access_token;
40
+ }
41
+ }
42
+ export class GatewayError extends Error {
43
+ code;
44
+ constructor(message, code) {
45
+ super(message);
46
+ this.code = code;
47
+ this.name = 'GatewayError';
48
+ }
49
+ }
50
+ /** Cliente JSON-RPC do `POST /mcp/:subservice` (o gateway é stateless, sem sessão). */
51
+ export class GatewayClient {
52
+ cfg;
53
+ fetchImpl;
54
+ seq = 0;
55
+ constructor(cfg, fetchImpl = fetch) {
56
+ this.cfg = cfg;
57
+ this.fetchImpl = fetchImpl;
58
+ }
59
+ endpoint(subservice) {
60
+ return `${this.cfg.url.replace(/\/$/, '')}/mcp/${subservice}`;
61
+ }
62
+ /** Config de MCP remoto para o Claude Agent SDK, um por conector, com o token do agente. */
63
+ async mcpServers(subservices, tokens = this.cfg.tokens) {
64
+ const token = await tokens.token();
65
+ return Object.fromEntries(subservices.map((s) => [s, { type: 'http', url: this.endpoint(s), headers: { authorization: `Bearer ${token}` }, alwaysLoad: true }]));
66
+ }
67
+ async rpc(subservice, method, params, tokens = this.cfg.tokens) {
68
+ const res = await this.fetchImpl(this.endpoint(subservice), {
69
+ method: 'POST',
70
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${await tokens.token()}` },
71
+ body: JSON.stringify({ jsonrpc: '2.0', id: ++this.seq, method, params }),
72
+ });
73
+ if (res.status === 401 || res.status === 403)
74
+ throw new GatewayError(`gateway recusou a identidade em ${subservice}`, res.status);
75
+ const json = (await res.json());
76
+ if (json.error)
77
+ throw new GatewayError(json.error.message, json.error.code);
78
+ return json.result;
79
+ }
80
+ async listTools(subservice, tokens) {
81
+ const r = await this.rpc(subservice, 'tools/list', {}, tokens);
82
+ return r.tools ?? [];
83
+ }
84
+ /** Executa uma ação já aprovada (proposta do copilot), sem passar pelo modelo. */
85
+ async callTool(subservice, name, args) {
86
+ const r = await this.rpc(subservice, 'tools/call', {
87
+ name,
88
+ arguments: args,
89
+ });
90
+ return { ok: !r.isError, content: r.structuredContent ?? r.content };
91
+ }
92
+ }
93
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
package/dist/host.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
2
+ import type { Brain } from './brain.js';
3
+ import type { GatewayClient } from './gateway.js';
4
+ import { type IncomingEvent } from './inbox.js';
5
+ import type { LoadedAgent } from './manifest.js';
6
+ import { type Notifier } from './notifier.js';
7
+ import { type RoundReport } from './round.js';
8
+ import type { ActionRow, LongTermStore } from './store/long-term.js';
9
+ /** Executa uma ação aprovada, sem o modelo. Em produção, o gateway com a identidade do agente. */
10
+ export interface ToolExecutor {
11
+ call(agent: LoadedAgent, qualifiedTool: string, input: Record<string, unknown>): Promise<{
12
+ ok: boolean;
13
+ content: unknown;
14
+ }>;
15
+ }
16
+ export declare function gatewayExecutor(gateway: GatewayClient): ToolExecutor;
17
+ export interface AgentHostOptions {
18
+ agents: LoadedAgent[];
19
+ store: LongTermStore;
20
+ brain: Brain;
21
+ /** Conectores para o modelo. Em produção: `gateway.mcpServers(...)` com o token do agente. */
22
+ servers: (agent: LoadedAgent) => Promise<Record<string, McpServerConfig>>;
23
+ executor: ToolExecutor;
24
+ notifier?: Notifier;
25
+ pluginsDir?: string;
26
+ now?: () => Date;
27
+ }
28
+ export interface Asker {
29
+ email: string;
30
+ kind: 'person' | 'agent';
31
+ }
32
+ export declare class AgentHost {
33
+ private readonly o;
34
+ readonly agents: Map<string, LoadedAgent>;
35
+ private readonly skills;
36
+ private readonly crons;
37
+ private readonly notifier;
38
+ private http?;
39
+ constructor(o: AgentHostOptions);
40
+ get store(): LongTermStore;
41
+ agent(id: string): LoadedAgent;
42
+ private skillsOf;
43
+ enqueue(agentId: string, ev: IncomingEvent): Promise<{
44
+ itemId: number | null;
45
+ duplicate: boolean;
46
+ }>;
47
+ runRound(agentId: string, hops?: number): Promise<RoundReport>;
48
+ /**
49
+ * Conversa direta (pelo Claude, via conector do círculo, ou de outro agente). Corre à
50
+ * parte da fila. Só lê, com o acesso do próprio agente: ele é um colaborador virtual, e
51
+ * quem pode conversar com ele é decidido pelo grant do conector `agents_<círculo>`.
52
+ */
53
+ converse(agentId: string, asker: Asker, message: string, conversationId: string, hops?: number): Promise<{
54
+ answer: string;
55
+ costUsd: number;
56
+ }>;
57
+ pendingProposals(agentId: string): Promise<ActionRow[]>;
58
+ /** Uma pessoa aprova ou recusa a proposta. Aprovada, roda pelo gateway com a identidade do agente. */
59
+ decideProposal(actionId: number, decision: 'approved' | 'rejected', by: string, note?: string): Promise<ActionRow>;
60
+ /** Uma pessoa corrige o que ele fez. Vira aprendizado no próximo prompt e conta no disjuntor. */
61
+ correctAction(actionId: number, by: string, note: string): Promise<ActionRow>;
62
+ /** Incidente: a rotina desce na hora, por decisão de pessoa. */
63
+ demote(agentId: string, routine: string, to: 'shadow' | 'copilot' | 'agent', by: string, reason: string): Promise<void>;
64
+ /** Bloqueio de emergência: para tudo na hora, sem deploy. */
65
+ kill(agentId: string, by: string, reason: string): Promise<void>;
66
+ /** Liga o despertar de cada agente e a agenda das rotinas. */
67
+ startSchedules(onRound?: (agentId: string, r: RoundReport) => void): void;
68
+ /**
69
+ * Entrada de eventos por webhook: POST /agents/:id/events, assinado com HMAC-SHA256
70
+ * (`x-t4h-signature: sha256=<hex>`). GET /healthz para o Container App.
71
+ */
72
+ listen(port: number, hmacSecret: string | undefined): Promise<number>;
73
+ stop(): Promise<void>;
74
+ }
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;