Add TrueFoundryAgentStore for agents - #560
Conversation
🦋 Changeset detectedLatest commit: 8060262 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
1a5b818 to
45b8a9e
Compare
45b8a9e to
72c6a3f
Compare
45b880d to
2dcbc86
Compare
4af175c to
c9270c5
Compare
…n from main. TrueFoundry mode cannot run with STANDALONE=true.
5b5713f to
31377ea
Compare
f85a757 to
ac37e94
Compare
ac37e94 to
48f409f
Compare
48f409f to
99bf0fc
Compare
99bf0fc to
8060262
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8060262. Configure here.
| return row === undefined ? undefined : toRecord(row); | ||
| } | ||
|
|
||
| withTransaction<T>(fn: (transaction: Transaction<Database>) => Promise<T>): Promise<T> { |
There was a problem hiding this comment.
Why do we need this? Why can't we take db as an argument in the TrueFoundryAgentStore Constructor?
|
|
||
| async createAgent(input: CreateAgentInput, transaction?: TTransaction): Promise<AgentRecord> { | ||
| // Lets the DB unique constraint pick one winner for this tenant ID and name. | ||
| // Prevents concurrent requests from both creating the same remote agent. |
There was a problem hiding this comment.
This is only true if transaction is present. I do not see any code validating that. The type allows undefined.
| return this.#inner.updateAgent(input, transaction); | ||
| } | ||
|
|
||
| return this.#withUpdateLock(input, transaction, async txn => { |
There was a problem hiding this comment.
Please ensure we validate that the transaction is actually present here and is not undefined or absent.

Summary
Wire agent create/update/delete to ServiceFoundry in TrueFoundry mode via
TrueFoundryAgentStore(SF-first, remote id inexternal_id). Standalone stays DB-only.Linear: AGE-2064
Changes
TrueFoundryAgentStore+putRemoteAgent/deleteRemoteAgent/toPutRemoteAgentPayloadresolveAgentStore(same pattern as models/MCP)map*/to*convention insrc/truefoundry/AGENTS.mdHow was this tested?
tests/unit/truefoundry/TrueFoundryAgentStore.test.ts(create/update/delete + failure paths)resolveAgentStoretsc --noEmitfor@truefoundry/trueforgeChecklist
pnpm build,pnpm test,pnpm typecheck,pnpm lint:ci, andpnpm format:checkpass locallypackages/trueforge-sdk,.github/fern/openapi/openapi.json,docs/openapi.json) — fork PRs omit SDK regen; maintainers regenerate after merge.env.exampleupdated if configuration or behavior changed (N/A — no new env;AGENTS.mdmapper convention only)Note
Medium Risk
Agent CRUD now coordinates local Postgres with ServiceFoundry; partial failures rely on cleanup/restore logic, though standalone mode is unchanged.
Overview
In TrueFoundry mode, agent create/update/delete now goes through
TrueFoundryAgentStore, which keeps the local DB as source of truth but mirrors changes to ServiceFoundry (PUT/DELETEon/internal/tfg/agents) and persists the returnedagentIdinexternal_id. Create/update use transactional advisory locks and rollback paths when remote or local writes fail.Wiring matches models/MCP: a shared
TrueFoundryServiceFoundryServerClient(with separate 10s default vs 3s agent CRUD timeouts) and per-requestresolveAgentStoreso HTTP handlers use the token-bound decorator while the scheduler stays on DB-only persistence.listAgentsnow accepts an optionalexternal_idsfilter (store contract + Postgres/SQLite). Agent create rejects reserved namestfgandtrueforge, andAgentExternalIdConflictErrormaps to 409 alongside name conflicts.Reviewed by Cursor Bugbot for commit 8060262. Bugbot is set up for automated code reviews on this repo. Configure here.