@agent-builder-lab/eve-kit 0.2.0 → 0.4.0-rc.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.
package/dist/channel.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AuthFn } from 'eve/channels/auth';
2
- import { EveChannelInput, EveChannel, EveMessageContext, EveMessageResult } from 'eve/channels/eve';
2
+ import { EveChannelConfig, EveChannel, EveMessageContext, EveMessageResult } from 'eve/channels/eve';
3
3
 
4
- interface LabChannelOptions extends Omit<EveChannelInput, "auth"> {
4
+ interface LabChannelOptions extends Omit<EveChannelConfig, "auth"> {
5
5
  /**
6
6
  * Autenticadores do próprio agente, verificados antes do acesso local do lab. O acesso local
7
7
  * (`localDev()` do eve) entra sempre por último.
@@ -28,6 +28,9 @@ interface LabChannelOptions extends Omit<EveChannelInput, "auth"> {
28
28
  * para os atributos do auth do turno (`labAccount`), onde `labAuthorization()` confere cada
29
29
  * tool call, e também sai do contexto que o modelo vê: a identidade vem do harness, nunca do
30
30
  * texto.
31
+ * - Quando o pedido traz o modo do MCP da formação (`clientContext.labMcp`, o botão do lab), os
32
+ * ataques vão para os atributos do auth do turno (`labMcp`) e saem do contexto do modelo:
33
+ * `labMcpAtaquesOf(ctx)` (de `@agent-builder-lab/eve-kit/mcp`) lê na ferramenta.
31
34
  *
32
35
  * Em produção (`eve start`, Vercel) o acesso local não autentica nada e o canal se comporta como
33
36
  * o padrão com os autenticadores que você passar.
package/dist/channel.js CHANGED
@@ -1,3 +1,8 @@
1
+ import {
2
+ LAB_MCP_ATTRIBUTE,
3
+ LAB_MCP_CONTEXT_KEY,
4
+ labMcpAtaquesFromContext
5
+ } from "./chunk-Z56XW2U4.js";
1
6
  import {
2
7
  LAB_ACCOUNT_ATTRIBUTE,
3
8
  LAB_ACCOUNT_CONTEXT_KEY,
@@ -8,7 +13,7 @@ import {
8
13
  LAB_FORK_CONTEXT_KEY,
9
14
  fetchForkPlan,
10
15
  forkIdFromContext
11
- } from "./chunk-3IQOFBSQ.js";
16
+ } from "./chunk-I5XIFBNJ.js";
12
17
 
13
18
  // src/channel.ts
14
19
  import { localDev } from "eve/channels/auth";
@@ -31,11 +36,13 @@ function labOnMessage(options) {
31
36
  const context = ctx.eve.clientContext;
32
37
  const forkId = forkIdFromContext(context);
33
38
  const account = labAccountFromContext(context);
34
- if (!forkId && !account && !hasLabKeys(context)) return base;
39
+ const mcpAtaques = labMcpAtaquesFromContext(context);
40
+ if (!forkId && !account && mcpAtaques === void 0 && !hasLabKeys(context)) return base;
35
41
  const plan = forkId ? await fetchForkPlan(forkId, options.connectorUrl) : void 0;
36
42
  const attributes = {
37
43
  ...forkId ? { [LAB_FORK_ATTRIBUTE]: forkId } : {},
38
- ...account ? { [LAB_ACCOUNT_ATTRIBUTE]: JSON.stringify(account) } : {}
44
+ ...account ? { [LAB_ACCOUNT_ATTRIBUTE]: JSON.stringify(account) } : {},
45
+ ...mcpAtaques !== void 0 ? { [LAB_MCP_ATTRIBUTE]: JSON.stringify({ ataques: mcpAtaques }) } : {}
39
46
  };
40
47
  return {
41
48
  ...base,
@@ -68,7 +75,11 @@ function isLoopback(request) {
68
75
  const host = new URL(request.url).hostname.replace(/^\[|\]$/g, "");
69
76
  return host === "127.0.0.1" || host === "localhost" || host === "::1";
70
77
  }
71
- var LAB_KEYS = [LAB_FORK_CONTEXT_KEY, LAB_ACCOUNT_CONTEXT_KEY];
78
+ var LAB_KEYS = [
79
+ LAB_FORK_CONTEXT_KEY,
80
+ LAB_ACCOUNT_CONTEXT_KEY,
81
+ LAB_MCP_CONTEXT_KEY
82
+ ];
72
83
  function hasLabKeys(context) {
73
84
  if (!context || typeof context !== "object" || Array.isArray(context)) return false;
74
85
  return LAB_KEYS.some((key) => key in context);
@@ -37,7 +37,7 @@ async function load(forkId, base) {
37
37
  });
38
38
  } catch {
39
39
  throw new Error(
40
- `Agent Builder Lab: o conector n\xE3o respondeu em ${base}. Ele est\xE1 rodando (agent-builder-lab connect)?`
40
+ `Agent Builder Lab: o conector n\xE3o respondeu em ${base}. Ele est\xE1 rodando (agent-builder-lab dev ou connect)?`
41
41
  );
42
42
  }
43
43
  const body = await response.json().catch(() => null);
@@ -0,0 +1,33 @@
1
+ // src/lab-mcp.ts
2
+ var LAB_MCP_CONTEXT_KEY = "labMcp";
3
+ var LAB_MCP_ATTRIBUTE = "labMcp";
4
+ var ATAQUES_VALIDOS = /^[a-z_@, ]{0,200}$/;
5
+ function labMcpAtaquesFromContext(context) {
6
+ if (!context || typeof context !== "object" || Array.isArray(context)) return void 0;
7
+ const value = context[LAB_MCP_CONTEXT_KEY];
8
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
9
+ const v = value;
10
+ if (v.v !== 1 || typeof v.ataques !== "string") return void 0;
11
+ return ATAQUES_VALIDOS.test(v.ataques) ? v.ataques : void 0;
12
+ }
13
+ function labMcpAtaquesFromAuth(auth) {
14
+ const raw = auth?.attributes?.[LAB_MCP_ATTRIBUTE];
15
+ if (typeof raw !== "string") return void 0;
16
+ try {
17
+ const ataques = JSON.parse(raw)?.ataques;
18
+ return typeof ataques === "string" && ATAQUES_VALIDOS.test(ataques) ? ataques : void 0;
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ }
23
+ function labMcpAtaquesOf(ctx) {
24
+ return labMcpAtaquesFromAuth(ctx.session.auth.current);
25
+ }
26
+
27
+ export {
28
+ LAB_MCP_CONTEXT_KEY,
29
+ LAB_MCP_ATTRIBUTE,
30
+ labMcpAtaquesFromContext,
31
+ labMcpAtaquesFromAuth,
32
+ labMcpAtaquesOf
33
+ };
package/dist/index.js CHANGED
@@ -95,6 +95,26 @@ function labInstrumentation(options = {}) {
95
95
  if (oldest !== void 0) sessions.delete(oldest);
96
96
  }
97
97
  }
98
+ const stepIndexes = /* @__PURE__ */ new Map();
99
+ function stepIndexOf(scope) {
100
+ const key = `${scope.sessionId}:${scope.turnId ?? ""}`;
101
+ let runs = stepIndexes.get(key);
102
+ if (!runs) {
103
+ runs = /* @__PURE__ */ new Map();
104
+ stepIndexes.delete(key);
105
+ stepIndexes.set(key, runs);
106
+ if (stepIndexes.size > MAX_SESSIONS) {
107
+ const oldest = stepIndexes.keys().next().value;
108
+ if (oldest !== void 0) stepIndexes.delete(oldest);
109
+ }
110
+ }
111
+ let index = runs.get(scope.runId);
112
+ if (index === void 0) {
113
+ index = runs.size;
114
+ runs.set(scope.runId, index);
115
+ }
116
+ return index;
117
+ }
98
118
  function base(idempotencyKey, scope) {
99
119
  const info = sessions.get(scope.sessionId);
100
120
  seq += 1;
@@ -105,9 +125,9 @@ function labInstrumentation(options = {}) {
105
125
  at: (/* @__PURE__ */ new Date()).toISOString(),
106
126
  sessionId: scope.sessionId,
107
127
  rootSessionId: scope.rootSessionId,
108
- turnId: scope.turnId,
109
- stepIndex: scope.stepIndex,
110
- attemptIndex: scope.attemptIndex,
128
+ turnId: scope.turnId ?? "",
129
+ stepIndex: stepIndexOf(scope),
130
+ attemptIndex: scope.attempt,
111
131
  agentName: info?.agentName,
112
132
  parentLineage: info?.parentLineage
113
133
  };
@@ -142,7 +162,15 @@ function labInstrumentation(options = {}) {
142
162
  }
143
163
  });
144
164
  },
145
- "model.call.completed": (event) => {
165
+ "model.call.settled": (event) => {
166
+ if (event.outcome !== "completed") {
167
+ send({
168
+ ...base(event.idempotencyKey, event.scope),
169
+ type: "model.call.failed",
170
+ error: error(event.error)
171
+ });
172
+ return;
173
+ }
146
174
  send({
147
175
  ...base(event.idempotencyKey, event.scope),
148
176
  type: "model.call.completed",
@@ -152,42 +180,35 @@ function labInstrumentation(options = {}) {
152
180
  content: event.content && [...event.content]
153
181
  });
154
182
  },
155
- "model.call.failed": (event) => {
156
- send({
157
- ...base(event.idempotencyKey, event.scope),
158
- type: "model.call.failed",
159
- error: error(event.error)
160
- });
161
- },
162
- "tool.call.started": (event, ctx) => {
183
+ "call.requested": (event, ctx) => {
163
184
  ctx.state.set({ callId: event.callId, toolName: event.toolName });
164
185
  send({
165
186
  ...base(event.idempotencyKey, event.scope),
166
187
  type: "tool.call.started",
167
188
  callId: event.callId,
168
189
  toolName: event.toolName,
169
- kind: event.kind,
190
+ kind: toolKind(event.capability.kind),
170
191
  input: event.input,
171
192
  startedAtMs: event.startedAtMs
172
193
  });
173
194
  },
174
- "tool.call.completed": (event, ctx) => {
175
- const started = ctx.state.get();
176
- send({
177
- ...base(event.idempotencyKey, event.scope),
178
- type: "tool.call.completed",
179
- callId: started?.callId,
180
- toolName: started?.toolName,
181
- outcome: event.outcome,
182
- // Prévias de imagem (data URLs) já chegam ao lab pelo stream do eve: aqui, só o tamanho.
183
- output: withoutInlineMedia(event.output),
184
- durationMs: event.durationMs,
185
- completedAtMs: event.completedAtMs,
186
- usage: event.usage && usage(event.usage)
187
- });
188
- },
189
- "tool.call.failed": (event, ctx) => {
195
+ "call.settled": (event, ctx) => {
190
196
  const started = ctx.state.get();
197
+ if (event.outcome === "completed") {
198
+ send({
199
+ ...base(event.idempotencyKey, event.scope),
200
+ type: "tool.call.completed",
201
+ callId: started?.callId,
202
+ toolName: started?.toolName,
203
+ outcome: event.outcome,
204
+ // Prévias de imagem (data URLs) já chegam ao lab pelo stream do eve: aqui, só o tamanho.
205
+ output: { type: "result", output: withoutInlineMedia(event.output) },
206
+ durationMs: event.durationMs,
207
+ completedAtMs: event.completedAtMs,
208
+ usage: event.usage && usage(event.usage)
209
+ });
210
+ return;
211
+ }
191
212
  send({
192
213
  ...base(event.idempotencyKey, event.scope),
193
214
  type: "tool.call.failed",
@@ -204,6 +225,12 @@ function labInstrumentation(options = {}) {
204
225
  flush: () => sender.flush(2e3)
205
226
  });
206
227
  }
228
+ function toolKind(kind) {
229
+ if (kind === "agent") return "subagent-call";
230
+ if (kind === "remote-agent") return "remote-agent-call";
231
+ if (kind === "skill") return "load-skill";
232
+ return "tool-call";
233
+ }
207
234
  function lineage(value) {
208
235
  return {
209
236
  sessionId: value.sessionId,
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,31 @@
1
1
  import { ApprovalPolicy } from 'eve/tools/approval';
2
2
 
3
+ /** Chave do `clientContext` com o modo do MCP do turno. */
4
+ declare const LAB_MCP_CONTEXT_KEY = "labMcp";
5
+ /** Atributo do auth do turno com os ataques, em JSON (`{"ataques":"injecao"}`), para as ferramentas lerem. */
6
+ declare const LAB_MCP_ATTRIBUTE = "labMcp";
7
+ /**
8
+ * Os ataques que o lab mandou no `clientContext` do pedido: "" para o modo limpo, "injecao",
9
+ * "todos"… Undefined quando o pedido não traz o modo ou ele vem fora do formato.
10
+ */
11
+ declare function labMcpAtaquesFromContext(context: unknown): string | undefined;
12
+ type AuthLike = {
13
+ readonly attributes?: Readonly<Record<string, string | readonly string[]>>;
14
+ };
15
+ /** Os ataques guardados nos atributos de um auth (o `labChannel()` grava). */
16
+ declare function labMcpAtaquesFromAuth(auth: AuthLike | null | undefined): string | undefined;
17
+ /**
18
+ * Os ataques do MCP escolhidos no lab para este turno, para usar dentro de uma ferramenta:
19
+ * `labMcpAtaquesOf(ctx)`. "" é o modo limpo; undefined, sem escolha (vale a URL do agente).
20
+ */
21
+ declare function labMcpAtaquesOf(ctx: {
22
+ readonly session: {
23
+ readonly auth: {
24
+ readonly current: AuthLike | null;
25
+ };
26
+ };
27
+ }): string | undefined;
28
+
3
29
  interface ClienteMcp {
4
30
  chamar(nome: string, argumentos: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>;
5
31
  listar(signal?: AbortSignal): Promise<Array<{
@@ -84,4 +110,4 @@ declare function aprovarEntradaExterna<TInput = Record<string, unknown>>(opcoes?
84
110
  readonly next?: ApprovalPolicy<TInput>;
85
111
  }): ApprovalPolicy<TInput>;
86
112
 
87
- export { type AchadoNoResultado, type ClienteMcp, type OpcoesDaFerramentaMcp, type OpcoesDeVarredura, type OpcoesDoCliente, type ResultadoDaVarredura, type ResultadoExterno, type SinalDeAtaque, aprovarEntradaExterna, chamarMcpVarrido, clienteMcpHttp, varrerResultado };
113
+ export { type AchadoNoResultado, type ClienteMcp, LAB_MCP_ATTRIBUTE, LAB_MCP_CONTEXT_KEY, type OpcoesDaFerramentaMcp, type OpcoesDeVarredura, type OpcoesDoCliente, type ResultadoDaVarredura, type ResultadoExterno, type SinalDeAtaque, aprovarEntradaExterna, chamarMcpVarrido, clienteMcpHttp, labMcpAtaquesFromAuth, labMcpAtaquesFromContext, labMcpAtaquesOf, varrerResultado };
package/dist/mcp.js CHANGED
@@ -1,3 +1,11 @@
1
+ import {
2
+ LAB_MCP_ATTRIBUTE,
3
+ LAB_MCP_CONTEXT_KEY,
4
+ labMcpAtaquesFromAuth,
5
+ labMcpAtaquesFromContext,
6
+ labMcpAtaquesOf
7
+ } from "./chunk-Z56XW2U4.js";
8
+
1
9
  // src/mcp-client.ts
2
10
  var PROTOCOLO = "2025-06-18";
3
11
  var ErroMcp = class extends Error {
@@ -270,8 +278,13 @@ function aprovarEntradaExterna(opcoes = {}) {
270
278
  };
271
279
  }
272
280
  export {
281
+ LAB_MCP_ATTRIBUTE,
282
+ LAB_MCP_CONTEXT_KEY,
273
283
  aprovarEntradaExterna,
274
284
  chamarMcpVarrido,
275
285
  clienteMcpHttp,
286
+ labMcpAtaquesFromAuth,
287
+ labMcpAtaquesFromContext,
288
+ labMcpAtaquesOf,
276
289
  varrerResultado
277
290
  };
package/dist/replay.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import * as eve from 'eve';
2
- import { AgentStaticModelDefinition, DynamicResolveContext } from 'eve';
2
+ import { AgentStaticModelDefinition, AgentReasoningDefinition, AgentModelOptionsDefinition, DynamicModelContext } from 'eve';
3
3
 
4
4
  /** O modelo de verdade: fixo ou escolhido a cada step, como num `defineDynamic`. */
5
5
  type LabModelChoice = AgentStaticModelDefinition | {
6
6
  model: AgentStaticModelDefinition;
7
7
  modelContextWindowTokens?: number;
8
- reasoning?: "provider-default" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
8
+ reasoning?: AgentReasoningDefinition;
9
+ modelOptions?: AgentModelOptionsDefinition;
9
10
  };
10
- type LabModelResolver = (event: unknown, ctx: DynamicResolveContext) => LabModelChoice | Promise<LabModelChoice>;
11
+ type LabModelResolver = (ctx: DynamicModelContext) => LabModelChoice | Promise<LabModelChoice>;
11
12
  interface LabReplayOptions {
12
13
  /** Porta local do conector. Padrão: `LAB_CONNECTOR_URL` ou http://127.0.0.1:2100. */
13
14
  connectorUrl?: string;
@@ -25,12 +26,12 @@ interface LabReplayOptions {
25
26
  * export default defineAgent({ model: labReplayModel("anthropic/claude-sonnet-5.5") });
26
27
  * ```
27
28
  *
28
- * Fora de um fork do lab, devolve o seu modelo em todo step. Num fork (o canal do lab marca o
29
+ * Fora de um fork do lab, devolve o seu modelo em todo step (ou o que a função `model(ctx)` escolher). Num fork (o canal do lab marca o
29
30
  * turno com o id), repete os steps gravados antes da volta editada, inclusive ela, com um
30
31
  * `mockModel`: mesmo texto, mesmas tool calls e mesmos ids. Assim as voltas anteriores saem
31
32
  * iguais às originais e o modelo de verdade só entra depois do ponto editado. O `modelId` desses
32
33
  * steps é `lab-replay/<modelo original>`.
33
34
  */
34
- declare function labReplayModel(model: LabModelChoice | LabModelResolver, options?: LabReplayOptions): eve.DynamicSentinel<LabModelChoice>;
35
+ declare function labReplayModel(model: LabModelChoice | LabModelResolver, options?: LabReplayOptions): eve.DynamicSentinel<LabModelChoice, DynamicModelContext>;
35
36
 
36
37
  export { type LabModelChoice, type LabModelResolver, type LabReplayOptions, labReplayModel };
package/dist/replay.js CHANGED
@@ -2,33 +2,32 @@ import {
2
2
  LAB_FORK_ATTRIBUTE,
3
3
  REPLAY_PROVIDER,
4
4
  fetchForkPlan
5
- } from "./chunk-3IQOFBSQ.js";
5
+ } from "./chunk-I5XIFBNJ.js";
6
6
 
7
7
  // src/replay.ts
8
- import { defineDynamic } from "eve";
8
+ import {
9
+ defineDynamic
10
+ } from "eve";
9
11
  import { mockModel } from "eve/evals";
12
+ import { runs } from "eve/events";
10
13
  function labReplayModel(model, options = {}) {
11
- const real = (event, ctx) => typeof model === "function" ? model(event, ctx) : model;
12
- return defineDynamic({
13
- events: {
14
- "step.started": async (event, ctx) => {
15
- const forkId = ctx.session.auth.current?.attributes[LAB_FORK_ATTRIBUTE];
16
- if (typeof forkId !== "string") return real(event, ctx);
17
- const plan = await fetchForkPlan(forkId, options.connectorUrl).catch(() => void 0);
18
- const step = plan?.replay?.steps[stepIndexOf(event)];
19
- if (!step) return real(event, ctx);
20
- return {
21
- model: replayStep(step, plan?.replay?.modelId),
22
- modelContextWindowTokens: options.replayContextWindowTokens ?? 1e6
23
- };
24
- }
25
- }
14
+ const real = (ctx) => typeof model === "function" ? model(ctx) : model;
15
+ return defineDynamic(async (ctx) => {
16
+ const forkId = ctx.session.auth.current?.attributes[LAB_FORK_ATTRIBUTE];
17
+ if (typeof forkId !== "string") return real(ctx);
18
+ const plan = await fetchForkPlan(forkId, options.connectorUrl).catch(() => void 0);
19
+ const step = plan?.replay?.steps[stepIndexOf(ctx)];
20
+ if (!step) return real(ctx);
21
+ return {
22
+ model: replayStep(step, plan?.replay?.modelId),
23
+ modelContextWindowTokens: options.replayContextWindowTokens ?? 1e6
24
+ };
26
25
  });
27
26
  }
28
- function stepIndexOf(event) {
29
- const record = event ?? {};
30
- const index = record.data?.stepIndex ?? record.stepIndex;
31
- return typeof index === "number" ? index : -1;
27
+ function stepIndexOf(ctx) {
28
+ const turnId = ctx.turn?.id;
29
+ if (!turnId) return -1;
30
+ return runs(ctx.view, { turnId }).filter((run) => run.status === "settled").length;
32
31
  }
33
32
  function replayStep(step, modelId = "modelo") {
34
33
  return mockModel({
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@agent-builder-lab/eve-kit",
3
- "version": "0.2.0",
3
+ "version": "0.4.0-rc.1",
4
4
  "description": "Liga um agente eve ao Agent Builder Lab: instrumentação da janela de contexto, canal com history e stubs, replay e autorização por conta de teste.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/vinilana/agent-builder-lab.git",
8
+ "directory": "packages/eve-kit"
9
+ },
5
10
  "type": "module",
6
11
  "exports": {
7
12
  ".": {
@@ -39,11 +44,11 @@
39
44
  "access": "public"
40
45
  },
41
46
  "peerDependencies": {
42
- "eve": ">=0.83.0 <0.84.0"
47
+ "eve": ">=1.0.0-rc.0 <2.0.0"
43
48
  },
44
49
  "devDependencies": {
45
50
  "@types/node": "24.19.2",
46
- "eve": "0.83.2",
51
+ "eve": "1.0.0-rc.2",
47
52
  "tsup": "8.5.1",
48
53
  "typescript": "6.0.3",
49
54
  "vitest": "5.0.5",