Codegen Internals
Why Generated Code is Installation-Specific
Generated gRPC code is never bundled in npm packages. Each
installation runs fit-codegen to produce code tailored
to its own set of services.
This design exists because external installations can define
custom proto files for custom gRPC services. Guide
dynamically picks up these services through the service management
system (fit-rc) and the tool configuration in
config/config.json. A custom proto file produces a
custom service client that Guide can use at runtime — without any
changes to the product code itself.
If generated code were bundled, it would only reflect the standard services and miss any custom services the installation defines.
Proto Distribution Model
Proto files are co-located with the packages that own them. Each
package includes a proto/ subdirectory published in its
npm tarball.
| Package | Proto files | Role |
|---|---|---|
@forwardimpact/guide |
common.proto, resource.proto |
Shared Guide types |
@forwardimpact/svcagent |
agent.proto |
Agent orchestration |
@forwardimpact/svcgraph |
graph.proto |
Graph queries |
@forwardimpact/svcllm |
llm.proto |
LLM inference |
@forwardimpact/svcmemory |
memory.proto |
Conversation memory |
@forwardimpact/svctool |
tool.proto |
Tool execution |
@forwardimpact/svctrace |
trace.proto |
Distributed tracing |
@forwardimpact/svcvector |
vector.proto |
Vector search |
| User project | proto/*.proto |
Custom services |
fit-codegen auto-discovers
proto/ subdirectories from all installed
@forwardimpact/* packages in
node_modules/, plus the project's own
proto/ directory. All discovered directories are used
as include paths so cross-file imports (e.g.
import "common.proto") resolve correctly.
Pipeline
Proto discovery → Type compilation → Service generation → Definition generation → Symlink
1. Proto discovery
CodegenBase.collectProtoFiles() scans all discovered
proto directories, deduplicates by filename, and ensures
common.proto is ordered first (other protos import it).
2. Type compilation
CodegenTypes compiles all proto files into a single
generated/types/types.js using the
pbjs compiler from protobufjs-cli. Proto
source files are also copied to generated/proto/ for
runtime loading.
3. Service and client generation
CodegenServices generates gRPC service base classes and
client stubs for each proto file that defines a service. Output
structure:
generated/services/
{service}/
service.js — Base class for the server implementation
client.js — Client stub for calling the service
exports.js — Aggregates all services and clients
4. Definition generation
CodegenDefinitions generates pre-compiled gRPC service
definitions used by librpc at runtime for serialization
and deserialization:
generated/definitions/
{service}.js — Pre-compiled service definition
exports.js — Aggregates all definitions
5. Symlink
Finder.createPackageSymlinks() creates symlinks from
librpc/generated/ and
libtype/generated/ to the central
generated/ directory, so relative imports in those
packages resolve correctly.
Module Index
| Module | Purpose |
|---|---|
base.js |
Shared utilities: proto parsing, template loading, rendering |
types.js |
Proto-to-JavaScript type compilation via pbjs
|
services.js |
Service base class and client stub generation |
definitions.js |
Pre-compiled gRPC definition generation |
bin/fit-codegen.js |
CLI entry point: discovery, orchestration, bundling |
templates/ |
Mustache templates for generated artifacts |
How librpc Consumes Generated Code
librpc imports generated code via relative paths:
-
librpc/index.jsimports./generated/services/exports.js(service bases and clients) -
librpc/base.jsimports./generated/definitions/exports.js(service definitions for gRPC registration)
These paths resolve through the symlink created by
fit-codegen during the symlink step.
Adding Custom Services
External users follow this workflow to add a custom gRPC service:
-
Define the proto — create
proto/custom.protoin the project root with a service definition -
Run codegen —
npx fit-codegen --alldiscovers the new proto automatically - Implement the server — create a service using the generated base class
-
Register the tool — add a tool endpoint mapping
in
config/config.jsonand a tool descriptor inconfig/tools.yml -
Use the client —
createClient("custom")fromlibrpcreturns a configured client
Guide's agent will automatically see the new tool through the dynamic tool configuration.
Internal vs External Workflow
| Step | Internal (monorepo) | External (npm) |
|---|---|---|
| Install | bun install (workspaces) |
npm install @forwardimpact/guide |
| Proto source |
services/*/proto/,
products/guide/proto/
|
node_modules/@forwardimpact/*/proto/ + project
proto/
|
| Run codegen |
just codegen or
bunx fit-codegen --all
|
npx fit-codegen --all |
| Output | generated/ at monorepo root |
generated/ at project root |
Related Documentation
- Guide Internals — Service stack that consumes generated code
- Operations Reference — Service management and environment setup