Skip to content

Conversation

@Paveltarno
Copy link
Contributor

@Paveltarno Paveltarno commented Jan 25, 2026

Description

This PR implements a complete agents integration system for the CLI, enabling users to define AI agents with custom instructions and tool configurations. The system provides push/pull commands to synchronize agent configurations between local JSON/JSONC files and Base44, includes comprehensive schema validation with Zod, and follows the established resource pattern used by entities and functions.

Related Issue

None

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe):

Changes Made

  • Added complete agent resource abstraction layer in src/core/resources/agent/ with schema validation, config reading/writing, and API integration
  • Implemented agents push command that replaces all remote agent configurations on Base44
  • Implemented agents pull command that fetches agents from Base44 and replaces all local agent files
  • Created agent configuration schema supporting entity tools (with granular CRUD permissions) and backend function tools
  • Integrated agent loading into project config reader with duplicate name detection
  • Added agentsDir field to project config schema (defaults to "agents")
  • Implemented API client methods pushAgents and fetchAgents for Base44 synchronization
  • Created comprehensive test fixtures for valid agents, invalid schemas, and duplicate names
  • Added unit tests covering agent reading, validation, API sync, and error cases (140+ lines of tests)
  • Added error handling utility formatApiError for consistent API error formatting across resources
  • Updated AGENTS.md documentation to reflect new agent resource architecture
  • Registered agents commands in CLI program

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated AGENTS.md if I made architectural changes

Additional Notes

Agent Configuration Format:

  • Agent names must be lowercase alphanumeric with underscores only (validated via regex: ^[a-z0-9_]+$)
  • Entity tool configs support granular operation permissions: read, create, update, delete
  • Backend function tool configs include custom descriptions for tool use
  • Optional WhatsApp greeting messages for conversational interfaces
  • Both JSON and JSONC file formats supported with comment preservation

Architecture Notes:

  • The push/pull commands use a full replacement strategy (not incremental sync) - pushing replaces all remote configs, pulling replaces all local files
  • This ensures deterministic state synchronization and follows the same pattern as the entities resource
  • Includes proper error handling for API failures and validation errors with clear user feedback
  • The implementation adds 633 lines across 29 files with comprehensive test coverage

🤖 Generated by Claude | 2026-01-27 09:52 UTC

@github-actions
Copy link
Contributor

github-actions bot commented Jan 25, 2026

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/[email protected]

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/[email protected]"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/[email protected]"
  }
}

Preview published to npm registry — try new features instantly!

@Paveltarno Paveltarno marked this pull request as ready for review January 26, 2026 14:06
@claude
Copy link

claude bot commented Jan 26, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@Paveltarno
Copy link
Contributor Author

@claude rebase from main

@claude
Copy link

claude bot commented Jan 26, 2026

Claude finished @Paveltarno's task in 15s —— View job


Task: Rebase from main

Unfortunately, I'm unable to perform rebase operations. My git capabilities are limited to basic operations like creating commits and pushing to branches, but I cannot perform branch operations like rebase, merge, or other complex git operations.

To rebase this branch from main yourself:

git checkout pavelta-agents-1
git fetch origin
git rebase origin/main
# Resolve any conflicts if they occur
git push --force-with-lease origin pavelta-agents-1

Alternatively, you could:

  • Use the GitHub UI to update the branch (if available in the PR interface)
  • Use your local git client or IDE to perform the rebase
  • Ask a team member with appropriate permissions to perform the rebase

Paveltarno and others added 7 commits January 27, 2026 11:49
Update command descriptions to make it clear that push replaces all
remote agent configurations and pull replaces all local agent files.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Paveltarno and others added 2 commits January 27, 2026 11:52
Fix TypeScript errors in CI by updating import path aliases to use
the correct @/ prefix instead of @ in agent-related files.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Move ApiErrorResponse interface and formatApiError function from
errors.ts to base44-client.ts to co-locate API-specific error
handling with the API client code.

- Moved API error types and formatting to base44-client.ts
- Updated imports in agent/api.ts and entity/api.ts
- Updated test imports and mocks
- Auth-related errors remain in errors.ts

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@kfirstri kfirstri moved this to Backlog in CLI Development Jan 27, 2026
@kfirstri kfirstri moved this from Backlog to In review in CLI Development Jan 27, 2026
* Standard API error response format from the Base44 backend.
* All HTTP errors from FastAPI are returned in this format.
*/
export interface ApiErrorResponse {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets move this to clients/schems.ts with a ApiErrorSchema and use optional / parse to stick to the structure of the project

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes been contemplating on where to put this

* Formats an API error response into a human-readable string.
* Prefers `message` (human-readable) over `detail`.
*/
export function formatApiError(errorJson: unknown): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try and catch and identify errors inside the ky client itself? and then we don't need to export this function and use it evereywhere - the base client will catch errors with this structure and throw correctly.. wdyt?
I'de say it's probably something to do in a different PR though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes good idea! as long as all errors are returned the same way from the API what you are suggesting is the correct approach

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we can either do it here, or resolve this in another pr and remove from this one..
Let me know what you choose and we i'll open an issue if needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's do it in another PR, @claude open an issue 😬

Copy link

@claude claude bot Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @Paveltarno's task in 44s —— View job


Task: Open issue for centralizing API error handling

Todo List:

  • Prepare issue content
  • Attempt to create issue

Result

I'm unable to create GitHub issues directly due to permission restrictions. However, I've prepared the issue content below that you can create manually:

Title: Centralize API error handling in base ky client

Body:

## Problem

Currently, API error handling is done manually by calling `formatApiError` in every place where we make API calls. This adds boilerplate and increases the chance of inconsistent error handling across the codebase.

**Current approach:**
```typescript
try {
  const response = await this.client.post(...);
  return response;
} catch (error) {
  throw new Error(formatApiError(error, 'operation description'));
}

Proposed Solution

Move error handling logic directly into the ky client itself, so that all API errors are automatically caught, identified, and formatted consistently. This would eliminate the need to export and use formatApiError everywhere.

Benefits:

  • Reduces boilerplate in API client methods
  • Ensures consistent error handling across all API calls
  • Centralizes error formatting logic
  • Makes the codebase easier to maintain

Implementation Notes

  • The base ky client should have hooks/interceptors to catch errors
  • Error structure should be identified and thrown with proper formatting automatically
  • The formatApiError function would become internal to the client
  • All existing usages of formatApiError can be removed

Context

This was discussed in PR #112 review:

Related


**Suggested labels:** `enhancement`, `refactor`

---

return {};
}

export const agentsCommand = new Command("agents")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets move the new "agents" command to be under commands/agents/index.ts, and each file will just export it's command

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general - do we need all of the schema?
We're trying to just zod-ify only things we actually use inside the CLI codebase, we don't want to verify the entire schema right now. might worth adding to AGENTS.md as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, do all the agent.json file as written as snake_case and not camelCase?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so you are suggesting we type it without zod and leave the "restrictions" to agents.md?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - I want to use zod, but only type the field that we need for CLI usage, like name and stuff..
I think we will need anyway to release jsonSchemas for all of our files and this will help the AI more then CLI errors

instructions: z.string(),
tool_configs: z.array(ToolConfigSchema).default([]),
whatsapp_greeting: z.string().nullable().optional(),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can look at other resources schema, lets use .transform to change response into camelCase

Comment on lines +61 to +70
for (const agent of agents) {
const filePath = join(agentsDir, `${agent.name}.${CONFIG_FILE_EXTENSION}`);
await writeJsonFile(filePath, {
name: agent.name,
description: agent.description,
instructions: agent.instructions,
tool_configs: agent.tool_configs,
whatsapp_greeting: agent.whatsapp_greeting ?? null,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should use an ejs file for agent.json file?
The main benefit is that we can give the user a file with comments inside in case they want to edit it..

we do something similar with .app.json file

export function generateAppConfigContent(id: string): string {
  return `// Base44 App Configuration
// This file links your local project to your Base44 app.
// Do not commit this file to version control.
{
  "id": "${id}"
}
`;
}

we can have another function with corrent agents structure, but also with more context about each field.. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure np, commented file is a good thing

agentsDir: string,
agents: AgentConfigApiResponse[]
): Promise<{ written: string[]; deleted: string[] }> {
const existingAgents = await readAllAgents(agentsDir);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think agents is something we should get as an argument, in the command that calls this function we already do getProjectConfig which reads all the agents anyway, so lets pass it to this function instead of reading it again


const toDelete = existingAgents.filter((a) => !newNames.has(a.name));
for (const agent of toDelete) {
const filePath = join(agentsDir, `${agent.name}.${CONFIG_FILE_EXTENSION}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: might be a bug if the AI / user will start creating agent config files using JSON and not JSONC, a bit of an edge case but it might be worth to globby and check for either endings >.>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea


if (!result.success) {
throw new Error(
`Invalid agent configuration in ${agentPath}: ${result.error.issues
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this example needs to be fixed in AGENTS.md file,
lets change to just use .parse in line 10 and let it throw, if alternative is to just use results.error.message

agents.length === 0
? "No local agents found - this will delete all remote agents"
: `Found ${agents.length} agents to push`
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to prompt the user in case of agents.length===0? we're writing "this will delete all remote agents" and then just going with it. how about a prompt and a --yes/-y flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm but didn't we say that for now we do not interrupt the push/pull in any way? because this is just an edge case in this whole conflict scenario
i suggest we either fully tackle it not at all
wdyt?

const { written, deleted } = await runTask(
"Writing agent files",
async () => {
return await writeAgents(agentsDir, response.items);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of passing the agentsDir, on line 10 you can do

const { project, agents } = await readProjectConfig();

and just pass the list of agent to the writeAgents function

const configDir = dirname(project.configPath);
const agentsDir = join(configDir, project.agentsDir);

const response = await runTask(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about call this remoteAgents?

log.warn(`Deleted: ${result.deleted.join(", ")}`);
}

return {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i saw that the pull command has an outroMessage, but the push command doesn't, lets align

Copy link
Contributor

@kfirstri kfirstri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work 💪

@github-project-automation github-project-automation bot moved this from In review to Ready in CLI Development Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

3 participants