Start / Project structure

Project structure & conventions

In Foundry the filesystem is the registry for code: a file’s path is its id, and code refers to other definitions by importing them. Runtime records — instances, conversations, installations, schedules — live in a data adapter, not in files. This page lists every file Foundry reads, recognizes or generates.

Standalone layout

npx glove-foundry init my-app creates a project that owns its own package.json. Grown to use every convention, it looks like this:

my-app/text
agents/
  support/
    agent.ts                              definition "support"            (discovered)
    composition.ts                        composeAgent(...) catalogue     (plain module)
    tools/lookup-order.tool.ts            tool "lookup-order"             (discovered)
    apps/helpdesk.app.ts                  application "helpdesk"          (discovered)
    transmissions/tickets.transmission.ts transmission "tickets"          (discovered)
    events/ticket-created.event.ts        event "ticket-created"          (discovered)
    predicates/is-urgent.predicate.ts     predicate "is-urgent"           (discovered)
    actions/resolve.action.ts             playbook action "resolve"       (discovered)
    connections/ticket-feed.connection.ts connection "ticket-feed"        (discovered)
    subscriptions/urgent.subscription.ts  playbook subscription "urgent"  (discovered)
    mcp/knowledge.mcp.ts                  MCP server "knowledge"          (discovered)
    memory/customer.memory.ts             memory profile "customer"       (discovered)
    layers/request-context.layer.ts       layer "request-context"         (discovered)
    subscribers/audit.subscriber.ts       subscriber "audit"              (discovered)
    schedules/follow-up.ts                defineSchedule value            (plain module)
    topology.ts                           accounts and routes             (plain module)
lib/                                      your helpers (stores, clients, models)
src/client.ts                             scripts that call the runtime
foundry.application.ts                    runtime infrastructure: data, stores, auth
foundry.config.ts                         runtime process settings
.foundry/routes.d.ts                      generated route types (commit it)
.foundry/manifest.json                    generated manifest (git-ignored)
.env.example  .env.local                  environment; .env.local is git-ignored
eslint.config.js  tsconfig.json  package.json

The generated package.json is "type": "module", requires Node >=22, and has four scripts: dev (glove foundry dev), start (glove foundry start), lint (eslint .) and typecheck (tsc --noEmit). Its tsconfig.json includes agents, lib, src, both root config files and .foundry/routes.d.ts. The minimal template starts with one agent (agents/assistant/) and one tool; travel-concierge has one example of each convention.

Next.js layout

npx glove foundry init . inside a Next.js app (detected from next.config.* or a next dependency, or forced with --target nextjs) joins the app instead of taking it over. Existing files are never overwritten.

PathWhat it is
foundry/agents/**The agents, same conventions as above
foundry/foundry.application.tsData adapter, accounts, routes
foundry/package.json{ "type": "module" } scoped to the agent tree, so the app keeps its own module system
foundry.config.mtsSets agentsDir: "foundry/agents" and applicationFile: "foundry/foundry.application.ts"
lib/foundry.tscreateFoundryClient<FoundryRoutes> with baseUrl: process.env.FOUNDRY_URL ?? "http://127.0.0.1:4141"
app/api/<agent>/route.tsExample POST handler: create instance, conversation, send, run.wait()
foundry/README.md, foundry.env.exampleGuide and environment template
package.jsonGains foundry:dev, foundry:start and the Glove dependencies; nothing else changes

No tsconfig.json or ESLint config is generated for this target; the app’s own apply. The runtime is a separate process (pnpm foundry:dev next to pnpm dev) and the app reaches it over HTTP. See Next.js integration.

File conventions

Agents. Foundry scans agentsDir (default agents) for .ts, .tsx, .mts, .js and .mjs files (not .d.ts):

FileAgent id
agents/support/agent.tssupport
agents/teams/billing/agent.tsteams/billing
agents/support.agent.tssupport (single-file form; prefer a folder)
agents/agent.tsdefault

An agent file default-exports defineAgent(...). Two files resolving to the same id fail discovery with Duplicate Foundry agent id.

Agent-local definitions. For each agent file, Foundry recursively scans the folder that contains it and binds every file whose name (before the extension) ends in one of these suffixes. Each must default-export one definition of the matching kind.

SuffixDefault exportHow it is used
*.tool.tsdefineSharedTool(...), or a bare Glove tool bodyCatalogue → installed per instance
*.app.tsdefineApp(...) (alias of defineAgentApplication)Catalogue → installed per instance
*.mcp.tsdefineMcp(...)Catalogue → installed per instance
*.memory.tsdefineMemory(...)Catalogue + memory field
*.layer.tsdefineLayer(...)Catalogue + layers field
*.subscriber.tsdefineSubscriber(...)Catalogue + subscribers field
*.transmission.tsdefineTransmission(...)Imported by apps and playbooks
*.event.tsdefineTransmissionEvent(...)Imported by transmissions and playbooks
*.predicate.tsdefineTransmissionPredicate(...)Imported by transmissions and playbooks
*.action.tsdefinePlaybookAction(...)Imported by composePlaybook directives
*.connection.tsdefineConnection(...)Imported by an app’s connections
*.subscription.tsdefinePlaybookSubscription(...)Background playbook policy

Not conventions. These are ordinary modules you import; Foundry never looks for them: composition.ts, schedules (defineSchedule values go in the schedules field — there is no .schedule.ts suffix), instances (defineAgentInstance), topology records (defineAccount, defineInboundRoute, defineOutboundRoute, defineBinding), working environments and REPLs, inbox loaders, typed calls and subagents. Root-level tools/, applications/, mcp/ or memory/ folders are not discovered: capabilities are agent-local.

How paths become ids

A local definition’s id is its path relative to the agent’s folder, minus the extension and suffix. If the first folder is one of the conventional names (tools, apps, mcp, memory, layers, subscribers, transmissions, events, predicates, actions, connections, subscriptions, playbooks), it is dropped. Deeper folders stay.

File (under agents/support/)Id
tools/lookup-order.tool.tslookup-order
tools/calendar/today.tool.tscalendar/today
lookup-order.tool.tslookup-order
billing/refund.tool.tsbilling/refund
apps/helpdesk/search.tool.tshelpdesk/search

The id is what installations reference ({ kind: "tool", id: "calendar/today" }). The name the model sees is the tool’s name. A bare tool body (an object with description, inputSchema and do, but no name) gets its name from the id: / becomes __ and - becomes _, so calendar/today is the tool calendar__today.

agents/support/tools/calendar/today.tool.tstypescript
import { z } from "zod";

// No defineSharedTool, no name: Foundry names it "calendar__today" from the path.
export default {
  description: "Return today's date in ISO format",
  inputSchema: z.object({}),
  async do() {
    return { status: "success" as const, data: new Date().toISOString().slice(0, 10) };
  },
};

Id rules

  • Ids are lowercase path segments of letters, digits and hyphens, starting with a letter, separated by / (^[a-z][a-z0-9-]*(/[a-z][a-z0-9-]*)*$). Name files in kebab-case: lookupOrder.tool.ts produces the id lookupOrder, and any run of an instance that installs it fails with Invalid Foundry tool id.
  • Agent ids are validated at discovery; an invalid folder name stops startup.
  • Do not pass id to file-routed helpers. The option still exists for programmatic definitions, but a mismatch with the file route throws, and the ESLint preset rejects it.
  • A definition that is not default-exported from a convention file inside an agent folder is never bound. Using it fails with Foundry tool identity has not been bound yet (or the matching kind).
  • Runtime records keep explicit ids because people and adapters create them: instances, conversations, accounts, routes and bindings. Code refers to them by value ({ account: supportAccount }), never by copying the string.

foundry.application.ts

Runtime infrastructure: where data lives and who may call the HTTP surface. It must default-export defineApplication(...). Foundry loads it in the runtime process and in every agent run’s worker process, so anything it creates must be safe to construct more than once. Without the file, Foundry uses an empty application with in-memory data.

OptionPurpose
nameRequired, non-empty
dataFoundryDataAdapter for instances, conversations, installations, playbooks, subscriptions, activations, workspace entries, inbox, tasks and VFS snapshots. Default MemoryFoundryDataAdapter (lost on restart); FileFoundryDataAdapter for one host
conversationStore(scope) => StoreAdapter used for transcripts when a definition has no store of its own
requestAuthorization{ identifier, challenge?, authorize(request) } returning Effect<boolean>; gates every HTTP request and is required to bind a non-loopback host
accounts, routes, bindingsIntegration topology from defineAccount, defineInboundRoute/defineOutboundRoute, defineBinding; duplicate ids throw
provisionerYour strategy for playbook subscriptions with provisioning.mode = "custom"
daemonstationDaemon(...) from glove-foundry/station for browser/sandbox resources
servicesEffect Layer replacing the in-memory AccountDirectory, TopologyStore and EventStore
foundry.application.tstypescript
import { FileFoundryDataAdapter, defineAgentInstance, defineApplication, install } from "glove-foundry";
import support from "./agents/support/agent.js";
import lookupOrder from "./agents/support/tools/lookup-order.tool.js";

const supportEu = defineAgentInstance(support, {
  id: "support-eu",
  workspaceId: "acme",
  installations: [install(lookupOrder, { region: "eu" })],
});

export default defineApplication({
  name: "Support desk",
  // Seeds are inserted only when the file has no instance with that id.
  data: new FileFoundryDataAdapter({ file: ".data/foundry.json", agents: [supportEu] }),
});

Agent definitions do not belong here; they are discovered from agents/. See Persistence and Deploy & security.

foundry.config.ts

Settings for the runtime process. Foundry looks for foundry.config.ts, .mts, .js or .mjs in the project root; it must default-export a value. defineConfig (from glove-foundry/config) makes unknown keys a type error. CLI flags --port and --host override the file.

KeyDefaultMeaning
agentsDir"agents"Where agents are discovered
applicationFile"foundry.application.ts"Path of the application file
server.host"127.0.0.1"Non-loopback requires requestAuthorization
server.port4141HTTP API, inspector and WebChat
server.messageBodyBytes1 MB (1048576)JSON limit for message-bearing endpoints; integer from 1048576 to 134217728
execution.maxConcurrent5Runs executing at once
execution.maxAttempts1Attempts per run
execution.retryBackoffMs1000Delay between attempts
execution.pollIntervalMs100Execution polling interval
execution.idlePollIntervalMs1000Deprecated; kept for source compatibility
observability.maxEvents10000Events retained for traces
branding.name / label / descriptor / accentGlove Foundry / — / Inspector / —Inspector shell text and a six-digit hex accent
strictFileRoutestrueReject an agent module whose exported id differs from its file route
foundry.config.tstypescript
import { defineConfig } from "glove-foundry/config";

export default defineConfig({
  server: { port: 4141, messageBodyBytes: 40 * 1024 * 1024 },
  execution: { pollIntervalMs: 100, maxConcurrent: 4 },
});

Generated files

Every glove foundry dev and start rewrites two files in .foundry/ before the server listens:

.foundry/routes.d.tstypescript
// Generated by Glove Foundry. Do not edit.
export type FoundryRoutes = {
  readonly "support": typeof import("../agents/support/agent.js");
};

Pass it to the client so route names are checked at compile time: createFoundryClient<FoundryRoutes>() makes foundry.agent("suport") a type error. It is a type-only import, so no agent code reaches the calling app. Commit it (the starter does), so typecheck works before the first run.

.foundry/manifest.json (version: 1) describes each discovered agent: id, description, file, tags, handler ("glove", or "custom" when run/spawn is set), the statically known names of tools, hooks, skills, subagents, memory, layers, subscribers, calls, schedules and playbooks, flags for inboxLoader, mesh, workingEnvironment and repl, and lazy — the fields given as resolvers, whose contents are only known per run. The starter git-ignores it. GET /api/manifest serves the same data plus each definition’s capability catalogue.

The ESLint preset

glove-foundry/eslint default-exports a flat-config object named glove-foundry/recommended that applies to **/*.{ts,tsx,mts,js,mjs}. The starter’s config:

eslint.config.jsjavascript
import tseslint from "typescript-eslint";
import foundry from "glove-foundry/eslint";

export default [
  { ignores: [".foundry/**"] },
  ...tseslint.configs.recommended,
  foundry,
];
Rule (all error)RejectsUse instead
glove-foundry/no-agent-contractsinput or output in defineAgent({…}), and export const input/outputNothing: Foundry owns the request and result contracts; use defineCall for typed operations
glove-foundry/no-file-definition-idAn id property in defineAgent, defineSharedTool, defineApp, defineAgentApplication, defineMcp, defineMemory, defineLayer, defineSubscriber, defineTransmission, defineTransmissionEvent, defineTransmissionPredicate, definePlaybookAction, defineConnection, definePlaybookSubscriptionThe filename
glove-foundry/no-raw-definition-referencesaccountId in install(def, config); transmissionId, applicationId, routeId, routeIds, applicationAccountId and string-literal event/action inside composePlaybookinstall(def, config, { account }); imported transmission, application, route(s), applicationAccount, event, action values

The plugin is also exported as foundryEslintPlugin and the preset as foundryEslintConfig.

Catalogue, installation, or definition field?

components: composeAgent(...) is a catalogue: it says which parts an instance may use. Nothing in it reaches the model until something else selects it. This table is the whole rule:

PartGoes inMounted on a run when
Plain Glove tool (name, description, inputSchema, do)tools fieldAlways, on every instance
Shared tool (*.tool.ts)composeAgentThe instance has a tool installation for its id
Application (*.app.ts)composeAgentThe instance has an application installation; its outbound transmissions become tools named glove_app_<app>__<transmission>_send, plus any tools its install hook returns
MCP server (*.mcp.ts)composeAgentThe instance has an mcp installation; the definition must also set mcpAdapter
Memory profile (*.memory.ts)composeAgent and the memory fieldListed in memory (directly or via configureMemory); no installation
Layer (*.layer.ts)composeAgent and the layers fieldListed in layers (or configureLayer); no installation
Subscriber (*.subscriber.ts)composeAgent and the subscribers fieldListed in subscribers; a raw SubscriberAdapter object needs no catalogue entry
Schedule (defineSchedule)schedules fieldReconciled into an activation on the instance’s next run
Typed call (defineCall)calls fieldAlways; also exposed as a tool unless exposeToAgent: false
Hooks, skills, subagentshooks, skills, subagents fieldsAlways
Built-in glove_foundry_* toolsNothingAlways: spawn, schedule, schedules, sleep, background, transmit, conversations, workspace, shared_inbox, tasks, environment

Selecting a memory profile, layer or subscriber that is missing from composeAgent fails the run (Unknown agent memory profile, references unknown layer). Installing an id that is missing from it is rejected (Unknown Foundry tool capability). Installations are added with install(def, config?, { account? }) in defineAgentInstance, installations on client.agent(route).create, client.install, or PUT /api/installations. Walk through it in the tutorial; every field is in Tools, MCP & memory.