Repository navigation
feat(types,agent): strict runtime validation for agent create/update DTOs - #350
Merged
Cjay-Cyber-2 merged 32 commits intoOct 7, 2026
Merged
Conversation
…ut error hierarchy (ASTROIDX556#80) Closes ASTROIDX556#80 Every resource method now forwards per-request `RequestOptionsExtras` (`signal`, `timeoutMs`, `retryable`, headers) to the HTTP client, and a deadline miss is raised as a first-class member of the SDK error hierarchy instead of a detached `Error` subclass. Error hierarchy - `AstroidTimeoutError` moves to `@astroid/errors` as a subclass of `NetworkError`, so `isAstroidError`, `err instanceof NetworkError`, `err.code`, `err.isRetryable`/`err.retryable` and `toJSON()` all work. `@astroid/core` re-exports it, keeping the published import path. - Adds the `isTimeoutError` guard and the `ApiErrorCode.REQUEST_TIMEOUT` code, mapped in `errorClassForCode` so server-reported timeouts and client-raised deadlines share one class. - Because a timeout carries no HTTP status it is classified as a transport failure, so it now participates in the existing backoff retry path instead of escaping the typed-error branch. Timeout config hardening - `normalizeTimeoutMs` coerces `0`, negative, `NaN` and `Infinity` to `DEFAULT_TIMEOUT_MS`, applied to both the resolved client config and the per-request override. A stray `timeout: 0` can no longer turn every request into an instant `AstroidTimeoutError`. Per-request options - Threads `RequestOptionsExtras` through every resource namespace (agent, analytics, auth, budget, notification, policy, transaction, wallet, webhook, ai) and the standalone `submitSignedTransaction` helper. Pagination helpers forward the options on every page fetch, so an `AbortSignal` cancels mid-iteration instead of only page one. - `PolicyResource.simulateTransaction` takes a second `requestOptions` argument because its first parameter is already an options object. Validation: pnpm build, pnpm typecheck (17/17), pnpm lint (0 errors) and pnpm test (1,718 passed, 0 failed) all pass.
…e() alias The merge of main introduced a two-argument simulatePolicy overload (rule, transaction) on PolicyResource. The deprecated simulate() alias called this.simulatePolicy(input, options), which TypeScript resolved against the (PolicyRule, TransactionDetails) overload and failed (TS2345: PolicySimulationRequest not assignable to PolicyRule). Call the simulatePolicy endpoint helper directly so request options are still forwarded as intended.
…DTOs (ASTROIDX556#215) Add zod schemas to @astroid/types that validate CreateAgentDto and UpdateAgentDto at runtime without duplicating the DTO types: - StellarPublicKeySchema (StrKey G..., 56 chars, base32 + checksum) - DecimalAmountStringSchema (no exponent/NaN/Infinity, >= 0) - AgentMetadataSchema (all values scalar, max 16 keys) - AgentInitialBudgetSchema (amount + optional assetId) - CreateAgentDtoSchema / UpdateAgentDtoSchema - isValid*/validate* helpers returning ValidationResult Wire the @astroid/agent create/update validators to the same rules via public assertion guards (assertValidStellarPublicKey, assertValidAmountString, validateAgentMetadata) so both layers reject identical payloads with AstroidValidationError {field, received, expected}. Tests: +32 in @astroid/types, +14 in @astroid/agent, including a 45-payload cross-layer agreement fixture table.
The test imported ../src/index.js, which resolves to packages/client/src/src/index.js and cannot exist, and imported isTimeoutError from ../index.js, which does not re-export it, so the workspace typecheck job failed on main (base commit 80da5e7). Import isTimeoutError from @astroid/errors and type the fetch stub parameters from Parameters<typeof fetch>. Test-only change; no production code touched.
|
@Tech-Cryptic Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
The merge of main into this branch left AstroidTimeoutError listed twice
in the same `import { ... } from './classes.js'` block — once after
AuthenticationError and once again after NetworkError. TypeScript
rejects that with TS2300 "Duplicate identifier", so the @astroid/errors
dts build failed and took the whole `build` job down with it. The
re-export list at the top of the file already exposes the class.
Also fix the import path in timeout-error-hierarchy.test.ts, which
imported '../src/index.js' from src/__tests__/ (resolving to the
nonexistent src/src/index.js) and left the fixture params untyped. Both
blocked `pnpm typecheck`, which never ran because it needs `build`.
…te and AgentResource.update PR ASTROIDX556#341 build failed with TS2554 in @astroid/policy: createPolicy() forwards `options` to create(), but create() only accepted the payload, so the dts build errored out. create() now takes the same RequestOptionsExtras every other method on the resource takes and passes it to the HTTP client. AgentResource.update() had the same gap and its tests already asserted the options argument, so those six tests were failing on the branch. It now accepts and forwards options too, and the deactivate/null-primaryWalletId assertions were updated to the three-argument call shape the implementation uses.
…DTOs (ASTROIDX556#215) Add zod schemas to @astroid/types that validate CreateAgentDto and UpdateAgentDto at runtime without duplicating the DTO types: - StellarPublicKeySchema (StrKey G..., 56 chars, base32 + checksum) - DecimalAmountStringSchema (no exponent/NaN/Infinity, >= 0) - AgentMetadataSchema (all values scalar, max 16 keys) - AgentInitialBudgetSchema (amount + optional assetId) - CreateAgentDtoSchema / UpdateAgentDtoSchema - isValid*/validate* helpers returning ValidationResult Wire the @astroid/agent create/update validators to the same rules via public assertion guards (assertValidStellarPublicKey, assertValidAmountString, validateAgentMetadata) so both layers reject identical payloads with AstroidValidationError {field, received, expected}. Tests: +32 in @astroid/types, +14 in @astroid/agent, including a 45-payload cross-layer agreement fixture table.
Contributor
|
…DTOs (ASTROIDX556#215) Add zod schemas to @astroid/types that validate CreateAgentDto and UpdateAgentDto at runtime without duplicating the DTO types: - StellarPublicKeySchema (StrKey G..., 56 chars, base32 + checksum) - DecimalAmountStringSchema (no exponent/NaN/Infinity, >= 0) - AgentMetadataSchema (all values scalar, max 16 keys) - AgentInitialBudgetSchema (amount + optional assetId) - CreateAgentDtoSchema / UpdateAgentDtoSchema - isValid*/validate* helpers returning ValidationResult Wire the @astroid/agent create/update validators to the same rules via public assertion guards (assertValidStellarPublicKey, assertValidAmountString, validateAgentMetadata) so both layers reject identical payloads with AstroidValidationError {field, received, expected}. Tests: +32 in @astroid/types, +14 in @astroid/agent, including a 45-payload cross-layer agreement fixture table.
…put-validation fix(agent): validate creation DTOs against issue requirements
….update and simulatePolicy PR ASTROIDX556#341's build job failed with three TS errors from the same root cause: the per-request abort/timeout work threaded an `options` argument through call sites whose resource methods still had the older, narrower signatures. - BudgetResource.get() now accepts `options?: RequestOptionsExtras` and forwards it to `getData(path, undefined, options)`, so validateBudgetSpend()'s `this.get(budgetId, requestOptions)` compiles and the caller's signal/timeout actually reach the HTTP client (TS2554). - PolicyResource.update() now accepts and forwards `options` to `client.patch()`, matching what its `updatePolicy()` alias already forwarded (TS2554). - The standalone simulatePolicy() endpoint helper forwards its `options` argument to `client.post()` instead of dropping it, which also clears the unused-parameter error (TS6133). These match the shape already used by the sibling methods fixed in 9b1dce3 and by wallet/notification/webhook, so the aliases stay consistent with their targets. pnpm build, pnpm typecheck and pnpm lint all pass; @astroid/budget (145) and @astroid/policy (289) test suites pass.
…-timeout-abortcontroller feat(core,errors,client): per-request abort/timeout options and timeo…
…to-validation-upstream feat(types,agent): strict runtime validation for agent DTOs
Contributor
CI Checks Failed - Action Needed. typecheck and lint fail because the agent-schema test entrypoint has unused imports or declarations, including AgentInitialBudgetSchema, StellarPublicKeySchema, DecimalAmountStringSchema, UPDATE_AGENT_DTO_FIELDS, and related validation helpers. Remove unused imports or add the intended assertions, then rerun CI. |
…urce-methods feat(wallet): implement wallet resource methods
Fix README formatting by adding missing newline
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.
Summary
Adds strict, runtime input validation for the agent creation and update DTOs, so
a misconfigured agent payload is rejected client-side — with a descriptive,
field-addressed error — instead of costing a round trip and a server
422.@astroid/typesgains the missing schemas for the canonical agent DTOs:CreateAgentDtoSchema/UpdateAgentDtoSchema(plusAgentMetadataSchema,AgentInitialBudgetSchema,StellarPublicKeySchema,DecimalAmountStringSchema) and the matchingvalidateCreateAgentDto/validateUpdateAgentDto/isValidCreateAgentDto/isValidUpdateAgentDtohelpers.
@astroid/agentexports the remaining guards so consumers can reuse the samerules the resource methods apply:
validateAgentMetadata,isValidAgentMetadata,assertValidStellarPublicKeyandassertValidAmountString.Closes #215
Why
Autonomous agents are configured from many untrusted sources — JSON bodies,
forms, environment variables, LLM-generated payloads. TypeScript is erased at
compile time, so nothing stops
{ name: 42 }or a negative budget cap(
-50,"1e3","0x10") from being serialised over the wire. The two failuremodes this PR removes:
G…key that is the wrong length, wrongprefix (
S…secret key) or outside the base-32 alphabet would only be caughtby the API, after the request (and possibly a retry) was spent.
Number()accepts"1e3","0x10","Infinity"and""; the Astroid API does not. Amounts travel as decimalstrings to preserve precision, so only plain non-negative decimal notation is
accepted.
The rules are now defined once per package and proven to agree: the DTO schemas
in
@astroid/typesand the assertion guards in@astroid/agentare exercisedagainst the same fixture table, so the two layers cannot silently drift.
What changed
packages/types/src/schemas.tsSTELLAR_PUBLIC_KEY_PATTERNG[A-Z2-7]{55}shape check shared with the other packages.StellarPublicKeySchemaG…addresses; trims surrounding whitespace, rejects wrong prefix/length/alphabet.DecimalAmountStringSchema"0","500","1000.00"); rejects-50,1e3,0x10,"".AgentMetadataSchemaz.record), butteam/externalId/tags/stellarAddressare validated when present, with issue paths.AgentInitialBudgetSchemacurrency+ valid decimalamount.CreateAgentDtoSchemaPOST /v1/agentspayload: requiredname, non-emptycapabilities, requiredinitialBudget; validatesdescription,role,provider,model,primaryWalletId,metadatawhen supplied.UpdateAgentDtoSchemaPATCH /v1/agents/:idpayload: every field optional, at least one required (an empty PATCH is a guaranteed no-op),primaryWalletIdadditionally acceptsnullto detach a wallet.UPDATE_AGENT_DTO_FIELDSisValidCreateAgentDto/isValidUpdateAgentDtoCreateAgentDto/UpdateAgentDto.validateCreateAgentDto/validateUpdateAgentDtoValidationResult<T>helpers with structurederror.issues.Both DTO schemas are annotated as
z.ZodType<CreateAgentDto>/z.ZodType<UpdateAgentDto>, so the interfaces inagent.tsremain the singlesource of truth — the schemas are checked against them at compile time instead of
duplicating the type definitions.
packages/agent/src/validation.tsThe create/update assertion guards already existed; this PR adds the missing
public surface and tightens metadata:
assertValidStellarPublicKey(value, field = 'stellarAddress')andassertValidAmountString(value, field = 'amount')— the throwing companions ofthe existing
isValid*predicates, matching theassertValid*naming used by@astroid/transaction.validateAgentMetadata(value, field = 'metadata')(assertion) andisValidAgentMetadata(value)— the internal metadata check is now public andalso validates
team/externalIdas non-blank strings, so metadata rulesmatch
AgentMetadataSchemaexactly.rule.
packages/agent/src/index.tsExports the added guards (
assertValidStellarPublicKey,assertValidAmountString,validateAgentMetadata,isValidAgentMetadata)alongside the existing
validateCreateAgentParams/isValidCreateAgentParams/validateUpdateAgentParams/isValidUpdateAgentParamsso any of them can beused standalone (forms, CLI flags, fixtures) before a payload reaches a resource
method.
Tests
packages/types/src/schemas.test.ts(+32 tests): valid/invalid payloads forboth DTO schemas, every required field, negative/coercible amounts, malformed
keys, metadata keys and issue paths, plus type-guard narrowing and helpers.
packages/agent/src/__tests__/validation.test.ts(+14 tests): the new publicguards, and a fixture table asserting
@astroid/types' schemas and@astroid/agent's guards reach the same verdict on 23 create and 22 updatepayloads (this is the anti-drift guarantee).
Acceptance criteria
update DTOs —
CreateAgentDtoSchema/UpdateAgentDtoSchema(+AgentInitialBudgetSchema,AgentMetadataSchema,StellarPublicKeySchema,DecimalAmountStringSchema) in@astroid/types, and the reusablecreate/update/metadata guards in
@astroid/agent.missing or malformed — the agent guards throw
AstroidValidationErrorwith
{ field, received, expected }indetailsbefore the HTTP request isdispatched; the types helpers return
ZodErrors whose issuepaths name theoffending field (
initialBudget.amount,metadata.externalId, …).@astroid/agent— every guard above isexported from the package entry point.
payloads — 46 new tests across the two packages, including the cross-layer
agreement table.
G…) and required string metadata —prefix, length and base-32 alphabet are enforced; required
name,initialBudget.currency,capabilities[]entries and typed metadata keysmust be non-blank strings.
definitions — the schemas are annotated against the existing interfaces
(
z.ZodType<CreateAgentDto>), and the guards are type predicates(
asserts params is CreateAgentParams), so no payload type is redefined.How it was tested
The new tests cover, for both DTOs: minimal and fully-populated valid payloads, a
payload typed as the canonical interface, every required field (missing / blank /
wrong type), negative and coercible amounts, malformed Stellar keys (short,
S…prefix, lowercase, base-32-invalid), enum violations, metadata typing, the
empty-
PATCHno-op rule,nullwallet detach, issue/field paths, and that thetype guards narrow
unknownto the DTO types.Risk
Low. Validation is additive and local: valid payloads take exactly the same
request path as before, and the only behaviour change inside
@astroid/agentisthat
metadata.team/metadata.externalIdsupplied as non-strings are nowrejected client-side (they were previously passed through and rejected by the
API).
Notes on unrelated breakage on the base branch
Two pre-existing problems on
mainare worth flagging, since neither is caused bythis change and only one could be fixed safely here:
typecheckwas already failing onmain. The CI run for the base commit(
80da5e7, PR feat(core,errors): AstroidTimeoutError error hierarchy and normalizeTimeoutMs #347) is red: the newpackages/client/src/__tests__/timeout-error-hierarchy.test.tsimported../src/index.js— which resolves topackages/client/src/src/index.jsandcannot exist — and imported
isTimeoutErrorfrom../index.js, a module thatdoes not re-export it. The file also left two
fetchparameters implicitlytyped. Because that job gates every PR, this PR includes the three-line test
fix (
../index.js,isTimeoutErrorfrom@astroid/errors, parameter typestaken from
Parameters<typeof fetch>) as a separate commit. It touches noproduction code; without it the required check cannot pass on any branch.
packages/policy/__tests__/validation.test.tshas 4 pre-existingfailures (
expected NetworkError … to be an instance of ValidationError).They fail identically with this PR's changes stashed, live entirely in
@astroid/policy, and are deliberately left untouched as out of scope.@astroid/client's rate-limiter burst test is also timing-flaky underfull-suite parallelism and passes in isolation.
pnpm -F @astroid/client testandpnpm -F @astroid/policy testare notjobs in
.github/workflows/ci.yml, so neither affects the checks for this PR.