Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 2 additions & 59 deletions .github/workflows/setup-github-projects_Version3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,68 +19,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
# Verify gh CLI version is compatible (>=2.21.0) for native 'gh project' commands
version=$(gh --version | head -n1 | awk '{print $3}' 2>/dev/null || echo "0.0.0")
major=$(echo "$version" | cut -d. -f1)
minor=$(echo "$version" | cut -d. -f2)
if [ "$major" -lt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -lt 21 ]; }; then
echo "gh CLI >= 2.21.0 required. Current: $version" >&2
exit 1
fi

- name: Create Project with description
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PROJECT_NAME="Agent Builder Application"
PROJECT_DESC="Agent Builder Application - AI agent creation, testing, and deployment platform"

# Check if project exists
proj_list=$(gh project list --owner ${{ github.repository_owner }} --format json 2>&1)
rc=$?
if [ $rc -ne 0 ]; then
echo "gh project list failed: $proj_list" >&2
exit $rc
fi
PROJECT_ID=$(echo "$proj_list" | jq -r "[.[] | select(.title == \"$PROJECT_NAME\") | .number] | first // empty")

if [ -z "$PROJECT_ID" ]; then
echo "Creating new project..."
gh project create "$PROJECT_NAME" --owner ${{ github.repository_owner }} --body "$PROJECT_DESC"

# Re-check to ensure we obtained an ID, fail explicitly if not found
proj_list=$(gh project list --owner ${{ github.repository_owner }} --format json 2>&1)
rc=$?
if [ $rc -ne 0 ]; then
echo "gh project list failed after create: $proj_list" >&2
exit $rc
fi
PROJECT_ID=$(echo "$proj_list" | jq -r "[.[] | select(.title == \"$PROJECT_NAME\") | .number] | first // empty")
if [ -z "$PROJECT_ID" ]; then
echo "Failed to obtain PROJECT_ID for $PROJECT_NAME" >&2
exit 1
fi
fi

echo "PROJECT_ID=$PROJECT_ID" >> $GITHUB_ENV

- name: Reminder - Create views manually
run: |
echo "NOTE: GitHub Projects v2 views cannot be created via the gh CLI."
echo "Please create the following views manually in the GitHub web UI:"
echo " 1. 'Tasks Table' (Table view)"
echo " 2. 'Kanban Board' (Board view)"
echo " 3. 'Schedule Timeline' (Timeline view)"

- name: Backfill existing open issues
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PROJECT_ID: "4"
run: |
PROJECT_ID=${{ env.PROJECT_ID }}
REPO="${{ github.repository }}"

# Fetch all open issues via paginated API
Expand Down Expand Up @@ -109,8 +52,8 @@ jobs:
if: github.event_name == 'issues' || github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PROJECT_ID: "4"
run: |
PROJECT_ID=${{ env.PROJECT_ID }}
REPO="${{ github.repository }}"

if [ "${{ github.event_name }}" = "issues" ]; then
Expand Down
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,14 @@ ARCHITECTURE_DIAGRAM.txt
OLLAMA_QUICK_SUMMARY.txt
mcp.json
tmp_write_check.txt
agent_builder_complete_infrastructure_updated.png
agent_builder_complete_infrastructure.png
agent_builder_comprehensive_features.png
agent_builder_deployment_flow.png
agent_builder_infrastructure
agent_builder_mcp_servers.png
agent_builder_memory_architecture.png
agent_builder_security.png
agent_builder_registries.png
agent_builder_three_chat_system.png
agent_builder_tier_comparison.png
233 changes: 137 additions & 96 deletions README.md

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import type * as cloudFormationGenerator from "../cloudFormationGenerator.js";
import type * as codeGenerator from "../codeGenerator.js";
import type * as cognitoAuth from "../cognitoAuth.js";
import type * as constants from "../constants.js";
import type * as containerOrchestrator from "../containerOrchestrator.js";
import type * as conversationAnalysis from "../conversationAnalysis.js";
import type * as conversations from "../conversations.js";
import type * as crons from "../crons.js";
Expand All @@ -56,18 +55,21 @@ import type * as http from "../http.js";
import type * as interleavedReasoning from "../interleavedReasoning.js";
import type * as lambdaTesting from "../lambdaTesting.js";
import type * as lib_aws_cloudwatchClient from "../lib/aws/cloudwatchClient.js";
import type * as lib_aws_ecsClient from "../lib/aws/ecsClient.js";
import type * as lib_aws_credentials from "../lib/aws/credentials.js";
import type * as lib_aws_s3Client from "../lib/aws/s3Client.js";
import type * as lib_bedrockGate from "../lib/bedrockGate.js";
import type * as lib_cloudFormationGenerator from "../lib/cloudFormationGenerator.js";
import type * as lib_dynamicModelSwitching from "../lib/dynamicModelSwitching.js";
import type * as lib_fileGenerators from "../lib/fileGenerators.js";
import type * as lib_iterativeLoop from "../lib/iterativeLoop.js";
import type * as lib_jsonUtils from "../lib/jsonUtils.js";
import type * as lib_memoryStore from "../lib/memoryStore.js";
import type * as lib_messageExecutor from "../lib/messageExecutor.js";
import type * as lib_roles from "../lib/roles.js";
import type * as lib_strandsTools from "../lib/strandsTools.js";
import type * as lib_tierConfig from "../lib/tierConfig.js";
import type * as lib_tokenBilling from "../lib/tokenBilling.js";
import type * as lib_toolDispatch from "../lib/toolDispatch.js";
import type * as lib_unifiedModalitySwitching from "../lib/unifiedModalitySwitching.js";
import type * as localModelDetector from "../localModelDetector.js";
import type * as maintenance from "../maintenance.js";
Expand Down Expand Up @@ -150,7 +152,6 @@ declare const fullApi: ApiFromModules<{
codeGenerator: typeof codeGenerator;
cognitoAuth: typeof cognitoAuth;
constants: typeof constants;
containerOrchestrator: typeof containerOrchestrator;
conversationAnalysis: typeof conversationAnalysis;
conversations: typeof conversations;
crons: typeof crons;
Expand All @@ -167,18 +168,21 @@ declare const fullApi: ApiFromModules<{
interleavedReasoning: typeof interleavedReasoning;
lambdaTesting: typeof lambdaTesting;
"lib/aws/cloudwatchClient": typeof lib_aws_cloudwatchClient;
"lib/aws/ecsClient": typeof lib_aws_ecsClient;
"lib/aws/credentials": typeof lib_aws_credentials;
"lib/aws/s3Client": typeof lib_aws_s3Client;
"lib/bedrockGate": typeof lib_bedrockGate;
"lib/cloudFormationGenerator": typeof lib_cloudFormationGenerator;
"lib/dynamicModelSwitching": typeof lib_dynamicModelSwitching;
"lib/fileGenerators": typeof lib_fileGenerators;
"lib/iterativeLoop": typeof lib_iterativeLoop;
"lib/jsonUtils": typeof lib_jsonUtils;
"lib/memoryStore": typeof lib_memoryStore;
"lib/messageExecutor": typeof lib_messageExecutor;
"lib/roles": typeof lib_roles;
"lib/strandsTools": typeof lib_strandsTools;
"lib/tierConfig": typeof lib_tierConfig;
"lib/tokenBilling": typeof lib_tokenBilling;
"lib/toolDispatch": typeof lib_toolDispatch;
"lib/unifiedModalitySwitching": typeof lib_unifiedModalitySwitching;
localModelDetector: typeof localModelDetector;
maintenance: typeof maintenance;
Expand Down
22 changes: 12 additions & 10 deletions convex/agentAsToolGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { action } from "./_generated/server";
import { v } from "convex/values";
import { api } from "./_generated/api";
import type { Id } from "./_generated/dataModel";
import { escapePythonString, escapePythonTripleQuote } from "./constants";

/**
* Generate agent-as-tool wrapper code
Expand All @@ -21,7 +22,7 @@ export const generateAgentAsTool = action( {
const agent: any = await ctx.runQuery( api.agents.get, { id: args.agentId } );
if ( !agent ) throw new Error( "Agent not found" );

const toolName: string = agent.name.replaceAll( /[^a-zA-Z0-9_]/g, '_' ).toLowerCase();
const toolName: string = agent.name.replaceAll( /\W/g, '_' ).toLowerCase();
const toolCode = generateToolCode( agent.name, toolName, agent.description || "", args.agentId );

return {
Expand All @@ -42,8 +43,9 @@ function generateToolCode(
agentId: string
): string {
// Sanitize inputs to prevent template injection in generated Python code
const sanitize = ( s: string ) => s.replaceAll( '\\', "\\\\" ).replaceAll( '"""', String.raw`\"\"\"` );
const safeAgentName = sanitize( agentName );
const safeAgentName = escapePythonTripleQuote(agentName);
const safeDescription = escapePythonString(description || `Invoke ${agentName} agent to handle specialized tasks`);
const safeAgentNameDQ = escapePythonString(agentName);
return `"""
Agent-as-Tool: ${safeAgentName}
Auto-generated wrapper to use ${safeAgentName} as a tool in other agents.
Expand All @@ -56,11 +58,11 @@ from typing import Optional

@tool(
name="${toolName}",
description="${description || `Invoke ${agentName} agent to handle specialized tasks`}",
description="${safeDescription}",
parameters={
"task": {
"type": "string",
"description": "The task or question to send to ${agentName}",
"description": "The task or question to send to ${safeAgentNameDQ}",
"required": True
},
"context": {
Expand All @@ -72,17 +74,17 @@ from typing import Optional
)
async def ${toolName}(task: str, context: Optional[dict] = None) -> str:
"""
Invoke ${agentName} agent as a tool.
Invoke ${safeAgentName} agent as a tool.

This allows hierarchical agent architectures where one agent
can delegate tasks to specialized agents.

Args:
task: The task or question for ${agentName}
task: The task or question for ${safeAgentName}
context: Optional context dictionary

Returns:
str: Response from ${agentName}
str: Response from ${safeAgentName}
"""
try:
# Get platform API endpoint from environment
Expand Down Expand Up @@ -139,7 +141,7 @@ export const generateCoordinatorAgent = action( {
// Generate tool wrappers for each agent
const agentTools: Array<{ name: string; agentId: string; agentName: string; description: string }> = agents.map( ( agent: any ) => {
if ( !agent ) return null;
const toolName: string = agent.name.replaceAll( /[\W]/g, '_' ).toLowerCase();
const toolName: string = agent.name.replaceAll( /\W/g, '_' ).toLowerCase();
return {
name: toolName,
agentId: agent._id,
Expand Down Expand Up @@ -277,7 +279,7 @@ export const linkAgentsForCoordination = action( {
const child: any = await ctx.runQuery( api.agents.get, { id: childId } );
if ( !child ) return null;

const toolName: string = child.name.replaceAll( /[\W]/g, '_' ).toLowerCase();
const toolName: string = child.name.replaceAll( /\W/g, '_' ).toLowerCase();
return {
name: toolName,
type: "agent_tool",
Expand Down
Loading