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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A type-safe Scala.js wrapper for `@anthropic-ai/claude-agent-sdk`, with idiomati
## Status

- SDK baseline: `@anthropic-ai/claude-agent-sdk` `0.2.75`
- Current repo build version: `0.4.0`
- Current repo build version: `0.4.1`
- Scala version: `3.7.4`
- Preferred JS runtime: `bun`
- Runtime requirement: `ANTHROPIC_API_KEY`
Expand All @@ -25,20 +25,20 @@ For a compatibility inventory against the installed SDK baseline, see `docs/COMP

## Installation

Use the current published release when consuming from Maven Central. For local development against this repo, the build version is `0.4.0`.
Use the current published release when consuming from Maven Central. For local development against this repo, the build version is `0.4.1`.

### Mill

```scala
def ivyDeps = Seq(
mvn"com.tjclp::scalagent::0.4.0"
mvn"com.tjclp::scalagent::0.4.1"
)
```

### SBT

```scala
libraryDependencies += "com.tjclp" %%% "scalagent" % "0.4.0"
libraryDependencies += "com.tjclp" %%% "scalagent" % "0.4.1"
```

### Maven
Expand All @@ -47,7 +47,7 @@ libraryDependencies += "com.tjclp" %%% "scalagent" % "0.4.0"
<dependency>
<groupId>com.tjclp</groupId>
<artifactId>scalagent_sjs1_3</artifactId>
<version>0.4.0</version>
<version>0.4.1</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object agent extends ScalaJSModule with PublishModule {
// Publishing configuration - read from PUBLISH_VERSION env var (set by CI)
// Task.Input ensures env var is re-evaluated each run (not cached)
override def publishVersion = Task.Input {
Task.env.get("PUBLISH_VERSION").getOrElse("0.4.1-SNAPSHOT")
Task.env.get("PUBLISH_VERSION").getOrElse("0.4.1")
}

// Skip scaladoc generation - Scala.js facades cause doc errors
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "scalagent",
"version": "0.4.1-SNAPSHOT",
"version": "0.4.1",
"private": true,
"type": "module",
"description": "Scalagent - Type-safe Scala.js wrapper for the Claude Agent SDK",
Expand Down
33 changes: 27 additions & 6 deletions src/com/tjclp/scalagent/macros/ToolInputMacros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,34 @@ object ToolInputMacros:
'{ JsonSchema.obj().additionalProperties.build }

case t if t.typeSymbol.flags.is(Flags.Enum) =>
val enumCases = enumCaseNames(t)
val casesExpr = Expr(enumCases)
'{ JsonSchema.enumOf($casesExpr*) }
// Try to summon a user-provided ToolInput for this type first.
// This handles ADT enums with custom JSON encoding (e.g. BodyContent)
// where the wire format can't be inferred from the case names.
trySummonToolInput(t).getOrElse {
val enumCases = enumCaseNames(t)
val casesExpr = Expr(enumCases)
'{ JsonSchema.enumOf($casesExpr*) }
}

case t if t.typeSymbol.flags.is(Flags.Case) =>
generateSchemaExpr(caseClassFieldInfos(t))
trySummonToolInput(t).getOrElse {
generateSchemaExpr(caseClassFieldInfos(t))
}

case other =>
report.warning(s"Unknown type ${other.show}, using object schema")
'{ JsonSchema.obj().build }
// Try to summon a user-provided ToolInput before falling back
trySummonToolInput(other).getOrElse {
report.warning(s"Unknown type ${other.show}, using object schema")
'{ JsonSchema.obj().build }
}

/** Try to summon a ToolInput[T] given in scope and extract its schema. */
private def trySummonToolInput(using Quotes)(
tpe: quotes.reflect.TypeRepr
): Option[Expr[JsonSchema]] =
import quotes.reflect.*
tpe.asType match
case '[t] =>
Expr.summon[ToolInput[t]].map { ti =>
'{ $ti.jsonSchema }
}
8 changes: 8 additions & 0 deletions src/com/tjclp/scalagent/tools/ToolDef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ object JsonSchema:
def toRaw: js.Object =
js.Dynamic.literal(`type` = "string", `enum` = values.toJSArray).asInstanceOf[js.Object]

/** Union type (anyOf) — for types that accept multiple formats (e.g. string | array) */
final case class AnyOfType(schemas: List[JsonSchema]) extends JsonSchema:
def toRaw: js.Object =
js.Dynamic.literal(anyOf = schemas.map(_.toRaw).toJSArray).asInstanceOf[js.Object]

/** Object type */
final case class ObjectType(
properties: Map[String, JsonSchema],
Expand Down Expand Up @@ -361,6 +366,9 @@ object JsonSchema:
/** Enum schema */
def enumOf(values: String*): JsonSchema = EnumType(values.toList)

/** Union schema (anyOf) */
def anyOf(schemas: JsonSchema*): JsonSchema = AnyOfType(schemas.toList)

/** Object schema builder */
def obj(properties: (String, JsonSchema)*): ObjectBuilder =
ObjectBuilder(properties.toMap)
Expand Down
2 changes: 2 additions & 0 deletions src/com/tjclp/scalagent/tools/ZodFacade.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object Zod extends js.Object:
def `enum`(values: js.Array[String]): ZodType = js.native
def array(schema: ZodType): ZodType = js.native
def `object`(shape: js.Dictionary[ZodType]): ZodType = js.native
def union(types: js.Array[ZodType]): ZodType = js.native

@js.native
trait ZodType extends js.Object:
Expand Down Expand Up @@ -60,3 +61,4 @@ object ZodConverter:
case JsonSchema.EnumType(values) => Zod.`enum`(values.toJSArray)
case JsonSchema.ArrayType(items) => Zod.array(toZodType(items))
case obj: JsonSchema.ObjectType => Zod.`object`(toZodRawShape(obj))
case JsonSchema.AnyOfType(schemas) => Zod.union(schemas.map(toZodType).toJSArray)
Loading