Skip to content
Open
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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ AgentJido is a Phoenix 1.8 application for the Jido AI agent ecosystem.

- Elixir 1.18+
- PostgreSQL 14+
- Node.js 18+ (for asset compilation)

### Setup

Expand All @@ -26,7 +27,10 @@ AgentJido is a Phoenix 1.8 application for the Jido AI agent ecosystem.
git clone https://github.com/agentjido/agent_jido.git
cd agent_jido

# Install dependencies and setup database
# Install JavaScript dependencies
cd assets && npm install && cd ..

# Install Elixir dependencies and setup database
mix setup

# Start the Phoenix server
Expand Down
88 changes: 42 additions & 46 deletions assets/js/ash_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@
// Utility Types

// Resource schema constraint
type TypedSchema = {
export type TypedSchema = {
__type: "Resource" | "TypedMap" | "Union";
__primitiveFields: string;
};

// Utility type to convert union to intersection
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I,
) => void
? I
: never;

// Helper type to infer union field values, avoiding duplication between array and non-array unions
type InferUnionFieldValue<
export type InferUnionFieldValue<
UnionSchema extends { __type: "Union"; __primitiveFields: any },
FieldSelection extends any[],
> = UnionToIntersection<
Expand All @@ -41,56 +41,56 @@ type InferUnionFieldValue<
: FieldSelection[FieldIndex] extends Record<string, any>
? {
[UnionKey in keyof FieldSelection[FieldIndex]]: UnionKey extends keyof UnionSchema
? UnionSchema[UnionKey] extends { __array: true; __type: "TypedMap"; __primitiveFields: infer TypedMapFields }
? NonNullable<UnionSchema[UnionKey]> extends { __array: true; __type: "TypedMap"; __primitiveFields: infer TypedMapFields }
? FieldSelection[FieldIndex][UnionKey] extends any[]
? Array<
UnionToIntersection<
{
[FieldIdx in keyof FieldSelection[FieldIndex][UnionKey]]: FieldSelection[FieldIndex][UnionKey][FieldIdx] extends TypedMapFields
? FieldSelection[FieldIndex][UnionKey][FieldIdx] extends keyof UnionSchema[UnionKey]
? { [P in FieldSelection[FieldIndex][UnionKey][FieldIdx]]: UnionSchema[UnionKey][P] }
? FieldSelection[FieldIndex][UnionKey][FieldIdx] extends keyof NonNullable<UnionSchema[UnionKey]>
? { [P in FieldSelection[FieldIndex][UnionKey][FieldIdx]]: NonNullable<UnionSchema[UnionKey]>[P] }
: never
: never;
}[number]
>
> | null
: never
: UnionSchema[UnionKey] extends { __type: "TypedMap"; __primitiveFields: infer TypedMapFields }
: NonNullable<UnionSchema[UnionKey]> extends { __type: "TypedMap"; __primitiveFields: infer TypedMapFields }
? FieldSelection[FieldIndex][UnionKey] extends any[]
? UnionToIntersection<
{
[FieldIdx in keyof FieldSelection[FieldIndex][UnionKey]]: FieldSelection[FieldIndex][UnionKey][FieldIdx] extends TypedMapFields
? FieldSelection[FieldIndex][UnionKey][FieldIdx] extends keyof UnionSchema[UnionKey]
? { [P in FieldSelection[FieldIndex][UnionKey][FieldIdx]]: UnionSchema[UnionKey][P] }
? FieldSelection[FieldIndex][UnionKey][FieldIdx] extends keyof NonNullable<UnionSchema[UnionKey]>
? { [P in FieldSelection[FieldIndex][UnionKey][FieldIdx]]: NonNullable<UnionSchema[UnionKey]>[P] }
: never
: never;
}[number]
> | null
: never
: UnionSchema[UnionKey] extends TypedSchema
? InferResult<UnionSchema[UnionKey], FieldSelection[FieldIndex][UnionKey]>
: NonNullable<UnionSchema[UnionKey]> extends TypedSchema
? InferResult<NonNullable<UnionSchema[UnionKey]>, FieldSelection[FieldIndex][UnionKey]>
: never
: never;
}
: never;
}[number]
>;

type HasComplexFields<T extends TypedSchema> = keyof Omit<
export type HasComplexFields<T extends TypedSchema> = keyof Omit<
T,
"__primitiveFields" | "__type" | T["__primitiveFields"]
> extends never
? false
: true;

type ComplexFieldKeys<T extends TypedSchema> = keyof Omit<
export type ComplexFieldKeys<T extends TypedSchema> = keyof Omit<
T,
"__primitiveFields" | "__type" | T["__primitiveFields"]
>;

type LeafFieldSelection<T extends TypedSchema> = T["__primitiveFields"];
export type LeafFieldSelection<T extends TypedSchema> = T["__primitiveFields"];

type ComplexFieldSelection<T extends TypedSchema> = {
export type ComplexFieldSelection<T extends TypedSchema> = {
[K in ComplexFieldKeys<T>]?: T[K] extends {
__type: "Relationship";
__resource: infer Resource;
Expand Down Expand Up @@ -119,17 +119,17 @@ type ComplexFieldSelection<T extends TypedSchema> = {
: T[K] extends { __type: "Union"; __primitiveFields: infer PrimitiveFields }
? T[K] extends { __array: true }
? (PrimitiveFields | {
[UnionKey in keyof Omit<T[K], "__type" | "__primitiveFields" | "__array">]?: T[K][UnionKey] extends { __type: "TypedMap"; __primitiveFields: any }
? T[K][UnionKey]["__primitiveFields"][]
: T[K][UnionKey] extends TypedSchema
? UnifiedFieldSelection<T[K][UnionKey]>[]
[UnionKey in keyof Omit<T[K], "__type" | "__primitiveFields" | "__array">]?: NonNullable<T[K][UnionKey]> extends { __type: "TypedMap"; __primitiveFields: any }
? NonNullable<T[K][UnionKey]>["__primitiveFields"][]
: NonNullable<T[K][UnionKey]> extends TypedSchema
? UnifiedFieldSelection<NonNullable<T[K][UnionKey]>>[]
: never;
})[]
: (PrimitiveFields | {
[UnionKey in keyof Omit<T[K], "__type" | "__primitiveFields">]?: T[K][UnionKey] extends { __type: "TypedMap"; __primitiveFields: any }
? T[K][UnionKey]["__primitiveFields"][]
: T[K][UnionKey] extends TypedSchema
? UnifiedFieldSelection<T[K][UnionKey]>[]
[UnionKey in keyof Omit<T[K], "__type" | "__primitiveFields">]?: NonNullable<T[K][UnionKey]> extends { __type: "TypedMap"; __primitiveFields: any }
? NonNullable<T[K][UnionKey]>["__primitiveFields"][]
: NonNullable<T[K][UnionKey]> extends TypedSchema
? UnifiedFieldSelection<NonNullable<T[K][UnionKey]>>[]
: never;
})[]
: NonNullable<T[K]> extends TypedSchema
Expand All @@ -138,12 +138,12 @@ type ComplexFieldSelection<T extends TypedSchema> = {
};

// Main type: Use explicit base case detection to prevent infinite recursion
type UnifiedFieldSelection<T extends TypedSchema> =
export type UnifiedFieldSelection<T extends TypedSchema> =
HasComplexFields<T> extends false
? LeafFieldSelection<T> // Base case: only primitives, no recursion
: LeafFieldSelection<T> | ComplexFieldSelection<T>; // Recursive case

type InferFieldValue<
export type InferFieldValue<
T extends TypedSchema,
Field,
> = Field extends T["__primitiveFields"]
Expand Down Expand Up @@ -279,20 +279,16 @@ type InferFieldValue<
: never
: T[K] extends { __type: "Union"; __primitiveFields: any }
? T[K] extends { __array: true }
? {
[CurrentK in K]: T[CurrentK] extends { __type: "Union"; __primitiveFields: any }
? Field[CurrentK] extends any[]
? Array<InferUnionFieldValue<T[CurrentK], Field[CurrentK]>> | null
: never
: never
}
: {
[CurrentK in K]: T[CurrentK] extends { __type: "Union"; __primitiveFields: any }
? Field[CurrentK] extends any[]
? InferUnionFieldValue<T[CurrentK], Field[CurrentK]> | null
: never
: never
}
? Field[K] extends any[]
? null extends T[K]
? Array<InferUnionFieldValue<T[K], Field[K]>> | null
: Array<InferUnionFieldValue<T[K], Field[K]>>
: never
: Field[K] extends any[]
? null extends T[K]
? InferUnionFieldValue<T[K], Field[K]> | null
: InferUnionFieldValue<T[K], Field[K]>
: never
: NonNullable<T[K]> extends TypedSchema
? null extends T[K]
? InferResult<NonNullable<T[K]>, Field[K]> | null
Expand All @@ -302,7 +298,7 @@ type InferFieldValue<
}
: never;

type InferResult<
export type InferResult<
T extends TypedSchema,
SelectedFields extends UnifiedFieldSelection<T>[] | undefined,
> = SelectedFields extends undefined
Expand All @@ -319,14 +315,14 @@ type InferResult<

// Pagination conditional types
// Checks if a page configuration object has any pagination parameters
type HasPaginationParams<Page> =
export type HasPaginationParams<Page> =
Page extends { offset: any } ? true :
Page extends { after: any } ? true :
Page extends { before: any } ? true :
false;

// Infer which pagination type is being used from the page config
type InferPaginationType<Page> =
export type InferPaginationType<Page> =
Page extends { offset: any } ? "offset" :
Page extends { after: any } | { before: any } ? "keyset" :
never;
Expand All @@ -335,7 +331,7 @@ type InferPaginationType<Page> =
// For single pagination type support (offset-only or keyset-only)
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type ConditionalPaginatedResult<
export type ConditionalPaginatedResult<
Page,
RecordType,
PaginatedType
Expand All @@ -347,7 +343,7 @@ type ConditionalPaginatedResult<

// For actions supporting both offset and keyset pagination
// Infers the specific pagination type based on which params were passed
type ConditionalPaginatedResultMixed<
export type ConditionalPaginatedResultMixed<
Page,
RecordType,
OffsetType,
Expand Down Expand Up @@ -533,7 +529,7 @@ export function buildCSRFHeaders(headers: Record<string, string> = {}): Record<s
* Handles hooks, request configuration, fetch execution, and error handling
* @param config Configuration matching ActionConfig
*/
async function executeActionRpcRequest<T>(
export async function executeActionRpcRequest<T>(
payload: Record<string, any>,
config: ActionConfig
): Promise<T> {
Expand Down Expand Up @@ -584,7 +580,7 @@ async function executeActionRpcRequest<T>(
* Handles hooks, request configuration, fetch execution, and error handling
* @param config Configuration matching ValidationConfig
*/
async function executeValidationRpcRequest<T>(
export async function executeValidationRpcRequest<T>(
payload: Record<string, any>,
config: ValidationConfig
): Promise<T> {
Expand Down
3 changes: 2 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ config :agent_jido,
generators: [timestamp_type: :utc_datetime],
ash_domains: [AgentJido.Accounts, AgentJido.GitHub, AgentJido.Forge.Domain, AgentJido.Folio],
ash_authentication: [return_error_on_invalid_magic_link_token?: true],
mailer: [from_name: "Agent Jido"]
mailer: [from_name: "Agent Jido"],
forge_fake_sprite_client?: config_env() in [:dev, :test]

# Configure the endpoint
config :agent_jido, AgentJidoWeb.Endpoint,
Expand Down
1 change: 1 addition & 0 deletions lib/agent_jido/accounts.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule AgentJido.Accounts do
@moduledoc false
use Ash.Domain, otp_app: :agent_jido, extensions: [AshAdmin.Domain]

admin do
Expand Down
1 change: 1 addition & 0 deletions lib/agent_jido/accounts/api_key.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule AgentJido.Accounts.ApiKey do
@moduledoc false
use Ash.Resource,
otp_app: :agent_jido,
domain: AgentJido.Accounts,
Expand Down
1 change: 1 addition & 0 deletions lib/agent_jido/accounts/token.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule AgentJido.Accounts.Token do
@moduledoc false
use Ash.Resource,
otp_app: :agent_jido,
domain: AgentJido.Accounts,
Expand Down
1 change: 1 addition & 0 deletions lib/agent_jido/accounts/user.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule AgentJido.Accounts.User do
@moduledoc false
use Ash.Resource,
otp_app: :agent_jido,
domain: AgentJido.Accounts,
Expand Down
2 changes: 1 addition & 1 deletion lib/agent_jido/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ defmodule AgentJido.Application do
end

defp forge_dev_children do
if Mix.env() in [:dev, :test] do
if Application.get_env(:agent_jido, :forge_fake_sprite_client?, false) do
[{AgentJido.Forge.SpriteClient.Fake, []}]
else
[]
Expand Down
36 changes: 28 additions & 8 deletions lib/agent_jido/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,17 @@ defmodule AgentJido.Error do
@moduledoc "Error for invalid input parameters."
use Splode.Error, class: :invalid

@type t :: %__MODULE__{}

def message(%{message: message}), do: message
end

defmodule ValidationError do
@moduledoc "Error for validation failures."
use Splode.Error, class: :invalid

@type t :: %__MODULE__{}

def message(%{message: message, field: field}) when not is_nil(field) do
"Validation failed for #{field}: #{message}"
end
Expand All @@ -83,39 +87,55 @@ defmodule AgentJido.Error do
@moduledoc "Error for runtime execution failures."
use Splode.Error, class: :execution

@type t :: %__MODULE__{}

def message(%{message: message}), do: message
end

defmodule ConfigurationError do
@moduledoc "Error for configuration issues."
use Splode.Error, class: :config

@type t :: %__MODULE__{}

def message(%{message: message}), do: message
end

# Helper functions

@doc "Creates a validation error with optional details."
@spec validation_error(String.t(), map()) :: ValidationError.t()
@spec validation_error(String.t(), map() | keyword()) :: ValidationError.t()
def validation_error(message, details \\ %{}) do
ValidationError.exception(Map.merge(%{message: message}, details))
ValidationError.exception(merge_error_details(message, details))
end

@doc "Creates an invalid input error."
@spec invalid_input_error(String.t(), map()) :: InvalidInputError.t()
@spec invalid_input_error(String.t(), map() | keyword()) :: InvalidInputError.t()
def invalid_input_error(message, details \\ %{}) do
InvalidInputError.exception(Map.merge(%{message: message}, details))
InvalidInputError.exception(merge_error_details(message, details))
end

@doc "Creates an execution failure error."
@spec execution_error(String.t(), map()) :: ExecutionFailureError.t()
@spec execution_error(String.t(), map() | keyword()) :: ExecutionFailureError.t()
def execution_error(message, details \\ %{}) do
ExecutionFailureError.exception(Map.merge(%{message: message}, details))
ExecutionFailureError.exception(merge_error_details(message, details))
end

@doc "Creates a configuration error."
@spec config_error(String.t(), map()) :: ConfigurationError.t()
@spec config_error(String.t(), map() | keyword()) :: ConfigurationError.t()
def config_error(message, details \\ %{}) do
ConfigurationError.exception(Map.merge(%{message: message}, details))
ConfigurationError.exception(merge_error_details(message, details))
end

defp merge_error_details(message, details) when is_list(details) do
Keyword.merge([message: message], details)
end

defp merge_error_details(message, details) when is_map(details) do
Keyword.merge([message: message], Map.to_list(details))
end

defp merge_error_details(message, nil) do
[message: message]
end
end
1 change: 1 addition & 0 deletions lib/agent_jido/folio.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule AgentJido.Folio do
@moduledoc false
use Ash.Domain

resources do
Expand Down
Loading
Loading