Skip to content

Add type guards, constants, and registry lifecycle events#10

Merged
ryandao merged 3 commits intomainfrom
rin/core-types
Apr 14, 2026
Merged

Add type guards, constants, and registry lifecycle events#10
ryandao merged 3 commits intomainfrom
rin/core-types

Conversation

@ryandao
Copy link
Copy Markdown
Owner

@ryandao ryandao commented Apr 14, 2026

Summary

  • Const arrays + derived types: AGENT_FRAMEWORKS, AGENT_STATUSES, TASK_STATUSES as as const arrays — types derived from them for a single source of truth
  • Type guards: isAgentFramework(), isAgentStatus(), isTaskStatus(), isAgent() for runtime validation of API responses and user input
  • Registry events: on("registered"), on("unregistered"), on("synced") lifecycle hooks with off() and unsubscribe-function return from on()
  • New types: RetryConfig (for upcoming client retry support), TaskStatus, RegistryEvents, RegistryEventListener

Test plan

  • npx tsc --noEmit — clean typecheck
  • npx tsup — builds CJS/ESM/DTS without errors
  • npx vitest run — all 35 existing tests pass (no regressions)
  • New type guards and event emitter will be tested in task 4 (tests & docs)

Submitted by ✨ Rin (DevSquad) for task cmny3jw6f0005g5e0gmq1ehk8

ryandao and others added 3 commits April 13, 2026 21:09
Implements full TypeScript SDK with feature parity to the Python SDK:
- AgentQClient with full CRUD for agents, heartbeat, and task management
- @agent decorator for declarative agent registration (mirrors Python @agent)
- AgentRegistry singleton for collecting and bulk-syncing agents
- Comprehensive type definitions for all API models
- Custom error hierarchy (ApiError, ConfigError, NetworkError, TimeoutError)
- Dual CJS/ESM build via tsup with full .d.ts type declarations
- 35 unit tests covering client, decorators, registry, and errors
- README with usage examples, API reference, and development guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add ESLint config with TypeScript rules and consistent-type-imports
- Add .npmignore for cleaner published package
- Add CHANGELOG.md following Keep a Changelog format
- Add sideEffects: false for tree-shaking support
- Add package.json/exports entry for bundler compatibility
- Add repository, bugs, homepage metadata for npm registry
- Add dev, typecheck, test:coverage, lint:fix scripts
- Strengthen prepublishOnly to run lint + typecheck + tests before build
- Remove emitDecoratorMetadata (unused, caused @swc/core warning)
- Enable noUncheckedIndexedAccess and isolatedModules for stricter types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Types:
- Add AGENT_FRAMEWORKS, AGENT_STATUSES, TASK_STATUSES const arrays
  (derive types with `as const` for single source of truth)
- Add TaskStatus type (was inline "pending"|"running"|... in AgentTask)
- Add RetryConfig type for upcoming retry support in HTTP client
- Add RegistryEvents and RegistryEventListener types
- Add type guard functions: isAgentFramework, isAgentStatus,
  isTaskStatus, isAgent for runtime validation

Registry:
- Add event emitter with on()/off() for "registered", "unregistered",
  "synced" lifecycle events
- on() returns unsubscribe function for ergonomic cleanup
- Emit events on register, unregister, and syncAll operations

Exports:
- Re-export all new types, constants, and type guards from barrel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ryandao ryandao mentioned this pull request Apr 14, 2026
4 tasks
@ryandao
Copy link
Copy Markdown
Owner Author

ryandao commented Apr 14, 2026

✅ Code Review: PR #10 — "Add type guards, constants, and registry lifecycle events"

Reviewer: Theo (DevSquad)
Verdict: APPROVE — Well-structured implementation that delivers all acceptance criteria for core types and agent registration. The as const + derived types pattern, type guards, and registry event emitter are all idiomatic TypeScript.


What's good

  1. as const arrays as single source of truthAGENT_FRAMEWORKS, AGENT_STATUSES, TASK_STATUSES as as const arrays with types derived via indexed access ((typeof ARRAY)[number]) is the gold-standard TypeScript pattern. Eliminates duplication between runtime values and compile-time types.

  2. Type guards are properly typedisAgentFramework, isAgentStatus, isTaskStatus all narrow correctly via value is X return types. isAgent does structural validation of the shape — good for validating untrusted API responses.

  3. Registry event emitter is well-typed — Generic on<K extends keyof RegistryEvents>() ensures listeners receive the correct payload type. The () => void unsubscribe return from on() is ergonomic and aligns with modern patterns (React, SolidJS, etc.).

  4. Metadata immutability — Using Object.defineProperty with writable: false, configurable: false on the Symbol-keyed metadata prevents accidental mutation. Test confirms this.

  5. Clean barrel exportssrc/index.ts properly re-exports types (with type keyword for type-only exports per ESLint consistent-type-imports), constants, guards, and classes.

  6. Test coverage — 35 tests across 4 files cover client CRUD, decorators (auto-register, manual, metadata immutability), registry (singleton, register/unregister/clear/reset), and all error classes. Good edge case coverage (empty config, non-existent agents, etc.).

  7. CI — SDK lint passes. Server lint+test failure is pre-existing (not caused by this PR).


Non-blocking suggestions

  1. syncAll lacks partial failure handling (medium)
    If 5 agents are registered and the 3rd call fails, the first 2 succeeded on the server but the caller only sees the thrown error. The "synced" event never fires. Consider using Promise.allSettled and collecting partial results, or at minimum document that syncAll is all-or-nothing.

  2. clear() doesn't emit "unregistered" events (low)
    Calling clear() silently removes all agents. Consumers subscribed to "unregistered" won't be notified. Consider emitting for each agent before clearing, or adding a separate "cleared" event.

  3. Error.cause shadowing in AgentQNetworkError (low)
    The cause property is declared explicitly rather than using the standard ES2022 Error(message, { cause }) constructor. V8 error chain formatting won't display the chained error in stack traces. Consider passing { cause } to super() instead.

  4. isAgent should validate tags (low)
    The guard checks id, name, status, capabilities, createdAt, updatedAt but not tags. An API response with tags: null would pass the guard but fail at runtime when code tries to access agent.tags.someKey. Add: && typeof obj["tags"] === "object" && obj["tags"] !== null

  5. Framework list alignment with Python SDK (info)
    The AGENT_FRAMEWORKS array has 6 values but the Python SDK (PR Support popular agent frameworks without @agent decorator #12) also supports "llamaindex" and "haystack". Not a blocker — can be added later — but worth tracking for cross-SDK consistency.

  6. Sequential syncAll could be slow for large registries (low)
    Each agent is registered one-at-a-time. For users with 20+ agents, Promise.all or Promise.allSettled with a concurrency limit would be faster. Not blocking since most users will have a handful of agents.


Summary

The core types are clean and well-designed, the as const pattern is textbook, the decorator + registry + event system work together cohesively, and tests cover the happy paths well. All suggestions are non-blocking. Ship it! 🚀

Reviewed by Theo (DevSquad). Note: GitHub formal APPROVE unavailable because reviewer and PR author share the same GH account.

@ryandao ryandao merged commit 5d1925b into main Apr 14, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant