Redesign sati as plain Java client for v3 protocol#42
Open
Redesign sati as plain Java client for v3 protocol#42
Conversation
Remove all Micronaut dependencies. The library is now plain Java 21 with only gRPC, protobuf, and SLF4J. Architecture: - ExileClient: builder-based entry point, composes WorkStream + services - ExileConfig: immutable connection config (certs, endpoint) - WorkStreamClient: single bidirectional gRPC stream implementing the v3 WorkStream protocol (replaces GateClientJobQueue, GateClientEventStream, and SubmitJobResults) - JobHandler interface: integrations implement to process jobs, return results directly (no more calling gateClient.submitJobResults internally) - EventHandler interface: integrations implement to handle events - Domain service clients: thin wrappers around AgentService, CallService, RecordingService, ScrubListService, ConfigService gRPC stubs Removed: - Micronaut framework (context, core, serde, http, validation, aot) - GateClientAbstract, GateClientJobQueue, GateClientEventStream, GateClientPollEvents, GateClientJobStream (all replaced by WorkStreamClient) - PluginInterface god interface (replaced by JobHandler + EventHandler) - 20+ model wrapper classes (use proto types directly) - Config/DiagnosticsService (replaced by ExileConfig/ConfigService) - StructuredLogger/LogCategory (use SLF4J directly) - Jackson, Reactor, HikariCP, Jakarta, SnakeYAML dependencies - demo module (was Micronaut app with copy-pasted controllers) What integrations need to change: - Implement JobHandler (return results) instead of PluginInterface (void) - Implement EventHandler instead of PluginInterface event methods - Use ExileClient.builder() instead of manually creating GateClient + Plugin + GateClientJobQueue + GateClientEventStream - Use proto types directly instead of model wrappers - Use domain service clients (client.agents(), client.calls()) instead of GateClient for unary RPCs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No protobuf generated interfaces are exposed in the public API. Integrations only see plain Java records, enums, and java.time types. Added: - model/ package: Pool, Record, Field, Filter, Agent, Skill, CallType, AgentState, TaskData, Page<T> — all Java records - model/event/ package: AgentCallEvent, TelephonyResultEvent, AgentResponseEvent, TransferInstanceEvent, CallRecordingEvent, TaskEvent — Java records with java.time.Duration/Instant fields - internal/ProtoConverter: bidirectional conversion between proto types and the Java model. Only class that touches generated proto classes. - service/ServiceFactory: creates service instances with package-private constructors so ManagedChannel doesn't leak Changed: - JobHandler: methods take/return plain Java types (List<Pool>, Page<Record>, Map<String,Object>) instead of proto messages - EventHandler: methods take event records instead of proto messages - All 5 service clients: public methods use Java types only, constructors are package-private - WorkStreamClient: converts between proto and model at the boundary Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New public type StreamStatus with Phase enum tracking the stream lifecycle: IDLE → CONNECTING → REGISTERING → ACTIVE → RECONNECTING → CLOSED. Includes: clientId, connectedSince, lastDisconnect, lastError, inflight count, completedTotal, failedTotal, reconnectAttempts. Accessible via ExileClient.streamStatus() — returns a snapshot. Uses isHealthy() convenience method for simple health checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New module that eliminates the 400-500 line ConfigChangeWatcher copy-pasted across finvi, capone, latitude, and debtnet. Components: - ConfigParser: reads the Base64-encoded JSON config file (com.tcn.exiles.sati.config.cfg) and produces an ExileConfig. No Jackson dependency — minimal built-in JSON parser. - ConfigFileWatcher: watches /workdir/config for file changes using directory-watcher. Fires Listener callbacks on create/modify/delete. Handles directory discovery and creation. - CertificateRotator: checks certificate expiration and rotates via the gate config service. Writes rotated cert back to the config file (triggering watcher reload). - ExileClientManager: single-tenant lifecycle manager. Watches config file, creates/destroys ExileClient on changes, detects org changes, schedules cert rotation. One builder replaces the entire ConfigChangeWatcher + manual bean wiring. - MultiTenantManager: multi-tenant lifecycle manager for velosidy-like deployments. Polls a tenant provider, reconciles desired vs actual tenants, creates/destroys ExileClients. Dependencies: sati-core + directory-watcher (single external dep). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The api() dependency configuration requires the java-library plugin. Both modules were missing it, causing build failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace pre-built buf.build Maven artifacts with local code generation using the build.buf gradle plugin. Proto stubs are now generated from source at build time instead of depending on published artifacts. Changes: - Add build.buf plugin (v0.11.0) to core module - Add buf.yaml, buf.gen.yaml, buf.lock for code generation config - Copy v3 proto files into core/proto/ (source of truth) - Pin protoc java plugin to v28.3 and grpc java to v1.68.1 - Add java_multiple_files=true to all proto files (generates top-level classes instead of nested inner classes) - Bump protobuf-java to 4.28.3 (matches remote plugin output) - Move grpc-netty-shaded from runtimeOnly to implementation (ChannelFactory needs compile-time access) - Rename model.Record to model.DataRecord (avoids collision with java.lang.Record) - Fix WorkItem.task oneof field name collision (task→exile_task) - Remove exileapi Maven version pins from gradle.properties - Apply spotless and buf format fixes The build now generates stubs from proto source, so sati can build independently of whether exileapi v3 is published to buf.build BSR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
62 tests across 7 test classes, all passing. core module (53 tests): - BackoffTest: exponential growth, jitter bounds, cap at 30s, reset - ProtoConverterTest: round-trip conversion for Duration, Timestamp, CallType, AgentState, Pool (all statuses), DataRecord, Field, Filter (all operators), Struct/Map (nested, lists, nulls), TaskData, Agent (with/without connected party), Skill, and all event types (AgentCall, TelephonyResult, CallRecording, Task) - ModelTest: Page.hasMore(), record equality, enum value counts - StreamStatusTest: isHealthy() for all phases - ExileConfigTest: builder, default port, null validation config module (9 tests): - ConfigParserTest: Base64 encoded, raw JSON, missing port, missing endpoint, missing certs, garbage input, empty input, trailing newline, escaped newlines in certificate values Added grpc-testing and grpc-inprocess test dependencies to core. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace local buf proto generation with pre-built artifacts from buf.build BSR, now that exileapi v3 is merged to master. Changes: - Remove build.buf gradle plugin, local proto files, buf.yaml/gen/lock - Add exileapi version pins in gradle.properties pointing to the published v3 artifacts (commit 461190882f3b) - Update all Java imports from old sub-packages (tcnapi.exile.types.v3, tcnapi.exile.worker.v3, etc.) to flat package with BSR prefix (build.buf.gen.tcnapi.exile.v3) - Service files use FQN for proto types to avoid ambiguity with identically-named model classes (Pool, Agent, Filter, etc.) - Move buf Maven repo declaration to root allprojects block Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Remove all Micronaut dependencies. The library is now plain Java 21 with only gRPC, protobuf, and SLF4J.
Architecture:
Removed:
What integrations need to change: