Skip to content

connect/eve dynamic tools skipped on durable replay ("step function … not registered") #51

Description

@Phelickz

Summary

@github-tools/sdk/connect/eve’s one-liner (export default connectGithubTools(...)) registers tools via defineDynamic + buildEveToolMap inside the SDK package. On eve durable agents (Slack multi-turn sessions), those tools appear in the tool map briefly (or not at all on later turns) and then get stripped at step time with:

[eve:dynamic-tools] Dynamic tool "getFileContent" references step function
  "eve:framework-dynamic:github:getFileContent" which is not registered — skipping on this step.

Same for createIssue, searchCode, getRepository, etc. Static authored tools in the consumer (e.g. list_accessible_repos) keep working. The model correctly reports that only inventory is available.

This matches eve’s documented durable-tool contract: defineTool({ execute }) must be an inline function in an authored agent/tools/*.ts (or extension) file so the bundler transform can hoist __executeStepFn / __closureVars. Eve’s workflow input scan ignores node_modules, so defineTool calls buried in @github-tools/sdk never get hoisted. Eve falls back to an in-process registerStepFunction on session.started; later workflow steps / isolates replay from metadata, look up an empty registry, and skip.

@github-tools/eve-extension (1.8.2) still does return buildEveToolMap(...) inside session.started, so it looks like the same footgun unless eve treats extension packages differently end-to-end.

Environment

  • eve 0.27.1
  • @github-tools/sdk 1.8.1 (also reviewed 1.8.2 / eve-extension source — same buildEveToolMap pattern)
  • @vercel/connect 0.3.3
  • Deployed on Vercel (serverless / Workflow steps)
  • Consumer: agent/tools/github.ts was:
import { connectGithubTools } from "@github-tools/sdk/connect/eve";

export default connectGithubTools("github/fnaagent-gh", {
  preset: ["repo-explorer", "issue-triage"],
  connect: { installationId: "…" },
});

Evidence

Production logs on a multi-turn Slack session (sessionId reused across turns):

[eve:dynamic-tools] Dynamic tool "getFileContent" references step function
  "eve:framework-dynamic:github:getFileContent" which is not registered — skipping on this step.
[eve:dynamic-tools] Dynamic tool "createIssue" references step function
  "eve:framework-dynamic:github:createIssue" which is not registered — skipping on this step.
… (entire preset map)

Audit: assistant states only list_accessible_repos is callable.

Relevant eve behavior (from dynamic-tool-lifecycle / docs):

  • Transform expects inline execute in authored sources (Dynamic capabilities — “execute must be an inline function”).
  • Runtime fallback registers eve:framework-dynamic:<slug>:<key> in-memory when __executeStepFn is missing.
  • Replay looks up that id later → miss → skip.

Workaround (works for us)

Keep using @github-tools/sdk/connect (AI SDK tools + Connect token), but put defineDynamic / defineTool in the consumer file with a for…of loop and inline execute that only closes over a serializable name, then resolves the AI SDK tool from a module-level map:

import { connectGithubTools } from "@github-tools/sdk/connect";
import { defineDynamic, defineTool } from "eve/tools";
import { always } from "eve/tools/approval";

const aiTools = connectGithubTools("github/…", {
  preset: ["repo-explorer", "issue-triage"],
  connect: { /* … */ },
});

async function runGithubAiTool(name: string, input: unknown) {
  const tool = aiTools[name];
  if (!tool?.execute) throw new Error(`GitHub tool ${name} has no execute`);
  return tool.execute(input, { toolCallId: name, messages: [], context: {} });
}

export default defineDynamic({
  events: {
    "session.started": async () => {
      const tools = {};
      for (const name of Object.keys(aiTools)) {
        tools[name] = defineTool({
          description: /* string */,
          inputSchema: aiTools[name].inputSchema,
          ...(writeTools.has(name) ? { approval: always() } : {}),
          execute: async (input) => runGithubAiTool(name, input),
        });
      }
      return tools;
    },
  },
});

After eve build, the bundle shows a hoisted step with __closureVars: { name } only (JSON-safe). Closing over the live Tool object must be avoided — durable replay serializes closure vars.

Ask

  1. Document that connect/eve / createGithubTools from @github-tools/sdk/eve is not durable under current eve unless defineTool is visible to the consumer transform (or fix the helper so it is).
  2. Prefer a supported recipe: either ship the authored-file pattern above in docs/examples, or change buildEveToolMap / connect/eve / eve-extension so tools are registered in a way eve’s durable transform (or Workflow "use step" registry) survives multi-isolate replay.
  3. If the intended fix is on eve (transform dependency defineDynamic / extension packages), please say so — happy to open/cross-link a vercel/eve issue. From the consumer side the broken advertised API is this package’s connect/eve one-liner.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions