diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13f2070..61ac64d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,20 @@ existing `0.1.0` release; earlier development prereleases are not listed.
## [Unreleased]
+### Fixed
+
+- Align Managed Session budgets with the official decimal-string `max_credit_cost` contract, shared by Deployment budgets. The limit covers cumulative model and sandbox Credits; reaching it rejects the next user message without interrupting the current turn.
+- Expose Managed and Forward Session `model_credits`, `sandbox_runtime_credits`, and `total_credits`; decode top-level `session.usage` Credits and per-call `model_usage.credits` / compaction `usage.credits`. Session usage may be absent. Legacy response token/list-cost fields remain available.
+- Route Identity ownership through query parameters on existing Forward Environment, Skill/Version, Vault/Credential, and File operations, including multipart uploads and content downloads.
+- Allow existing channel pairing creation with either a code or `channel_pairing_id`, and expose pairing scope details. No additional API endpoints are introduced.
+
+### Migration
+
+- **Breaking budget field change:** Replace `MaxListCost: managed.MonetaryAmountParam{...}` with `MaxCreditCost: "100.00"` in `ManagedAgentsBudgetLimitParam` (keep `Type: "limit"`). Amounts are positive decimal strings, not currency objects. Omitting a budget update preserves the limit; explicit null removes it.
+- Consume the three top-level Credits fields from `session.usage` events. Replace cumulative snapshots rather than adding them; compaction Credits are already included in Session totals.
+- Set `IdentityID: forward.String("idn_...")` in resource params; for methods without params, pass `forward.WithIdentityID("idn_...")`. Use this only with a PAT. With a SAT, omit `identity_id`; the server infers ownership from the token and rejects explicit values (including an empty string).
+- Channel pairing requests must provide exactly one of `code` or `channel_pairing_id`; existing code-based calls remain supported.
+
## [0.6.0]
### Added
diff --git a/docs/api/reference.md b/docs/api/reference.md
index 3d62b28..0cfc698 100644
--- a/docs/api/reference.md
+++ b/docs/api/reference.md
@@ -13,6 +13,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func Float\(v float64\) param.Opt\[float64\]](<#Float>)
- [func Int\(v int64\) param.Opt\[int64\]](<#Int>)
- [func String\(v string\) param.Opt\[string\]](<#String>)
+- [func WithIdentityID\(identityID string\) option.RequestOption](<#WithIdentityID>)
- [type Batch](<#Batch>)
- [func \(r Batch\) RawJSON\(\) string](<#Batch.RawJSON>)
- [func \(r \*Batch\) UnmarshalJSON\(data \[\]byte\) error](<#Batch.UnmarshalJSON>)
@@ -209,6 +210,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r EnvironmentListParams\) URLQuery\(\) \(url.Values, error\)](<#EnvironmentListParams.URLQuery>)
- [type EnvironmentNewParams](<#EnvironmentNewParams>)
- [func \(r EnvironmentNewParams\) MarshalJSON\(\) \(\[\]byte, error\)](<#EnvironmentNewParams.MarshalJSON>)
+ - [func \(r EnvironmentNewParams\) URLQuery\(\) \(url.Values, error\)](<#EnvironmentNewParams.URLQuery>)
- [func \(r \*EnvironmentNewParams\) UnmarshalJSON\(data \[\]byte\) error](<#EnvironmentNewParams.UnmarshalJSON>)
- [type EnvironmentService](<#EnvironmentService>)
- [func NewEnvironmentService\(opts ...option.RequestOption\) EnvironmentService](<#NewEnvironmentService>)
@@ -221,6 +223,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r \*EnvironmentService\) Update\(ctx context.Context, id string, params EnvironmentUpdateParams, opts ...option.RequestOption\) \(res \*Environment, err error\)](<#EnvironmentService.Update>)
- [type EnvironmentUpdateParams](<#EnvironmentUpdateParams>)
- [func \(r EnvironmentUpdateParams\) MarshalJSON\(\) \(\[\]byte, error\)](<#EnvironmentUpdateParams.MarshalJSON>)
+ - [func \(r EnvironmentUpdateParams\) URLQuery\(\) \(url.Values, error\)](<#EnvironmentUpdateParams.URLQuery>)
- [func \(r \*EnvironmentUpdateParams\) UnmarshalJSON\(data \[\]byte\) error](<#EnvironmentUpdateParams.UnmarshalJSON>)
- [type EnvironmentVariableOverride](<#EnvironmentVariableOverride>)
- [func \(r EnvironmentVariableOverride\) RawJSON\(\) string](<#EnvironmentVariableOverride.RawJSON>)
@@ -253,6 +256,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r \*FileService\) Upload\(ctx context.Context, params FileUploadParams, opts ...option.RequestOption\) \(res \*FileMetadata, err error\)](<#FileService.Upload>)
- [type FileUploadParams](<#FileUploadParams>)
- [func \(r FileUploadParams\) MarshalMultipart\(\) \(\[\]byte, string, error\)](<#FileUploadParams.MarshalMultipart>)
+ - [func \(r FileUploadParams\) URLQuery\(\) \(url.Values, error\)](<#FileUploadParams.URLQuery>)
- [type GitHubRepository](<#GitHubRepository>)
- [func \(r GitHubRepository\) RawJSON\(\) string](<#GitHubRepository.RawJSON>)
- [func \(r \*GitHubRepository\) UnmarshalJSON\(data \[\]byte\) error](<#GitHubRepository.UnmarshalJSON>)
@@ -761,6 +765,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r SkillListParams\) URLQuery\(\) \(url.Values, error\)](<#SkillListParams.URLQuery>)
- [type SkillNewParams](<#SkillNewParams>)
- [func \(r SkillNewParams\) MarshalMultipart\(\) \(\[\]byte, string, error\)](<#SkillNewParams.MarshalMultipart>)
+ - [func \(r SkillNewParams\) URLQuery\(\) \(url.Values, error\)](<#SkillNewParams.URLQuery>)
- [type SkillOverride](<#SkillOverride>)
- [func \(r SkillOverride\) RawJSON\(\) string](<#SkillOverride.RawJSON>)
- [func \(r \*SkillOverride\) UnmarshalJSON\(data \[\]byte\) error](<#SkillOverride.UnmarshalJSON>)
@@ -777,6 +782,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r \*SkillService\) Update\(ctx context.Context, id string, params SkillUpdateParams, opts ...option.RequestOption\) \(res \*Skill, err error\)](<#SkillService.Update>)
- [type SkillUpdateParams](<#SkillUpdateParams>)
- [func \(r SkillUpdateParams\) MarshalJSON\(\) \(\[\]byte, error\)](<#SkillUpdateParams.MarshalJSON>)
+ - [func \(r SkillUpdateParams\) URLQuery\(\) \(url.Values, error\)](<#SkillUpdateParams.URLQuery>)
- [func \(r \*SkillUpdateParams\) UnmarshalJSON\(data \[\]byte\) error](<#SkillUpdateParams.UnmarshalJSON>)
- [type SkillVersion](<#SkillVersion>)
- [func \(r SkillVersion\) RawJSON\(\) string](<#SkillVersion.RawJSON>)
@@ -785,6 +791,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r SkillVersionListParams\) URLQuery\(\) \(url.Values, error\)](<#SkillVersionListParams.URLQuery>)
- [type SkillVersionNewParams](<#SkillVersionNewParams>)
- [func \(r SkillVersionNewParams\) MarshalMultipart\(\) \(\[\]byte, string, error\)](<#SkillVersionNewParams.MarshalMultipart>)
+ - [func \(r SkillVersionNewParams\) URLQuery\(\) \(url.Values, error\)](<#SkillVersionNewParams.URLQuery>)
- [type SkillVersionService](<#SkillVersionService>)
- [func NewSkillVersionService\(opts ...option.RequestOption\) SkillVersionService](<#NewSkillVersionService>)
- [func \(r \*SkillVersionService\) Delete\(ctx context.Context, id string, version string, opts ...option.RequestOption\) \(res \*DeletedSkillVersion, err error\)](<#SkillVersionService.Delete>)
@@ -870,6 +877,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r VaultCredentialListParams\) URLQuery\(\) \(url.Values, error\)](<#VaultCredentialListParams.URLQuery>)
- [type VaultCredentialNewParams](<#VaultCredentialNewParams>)
- [func \(r VaultCredentialNewParams\) MarshalJSON\(\) \(\[\]byte, error\)](<#VaultCredentialNewParams.MarshalJSON>)
+ - [func \(r VaultCredentialNewParams\) URLQuery\(\) \(url.Values, error\)](<#VaultCredentialNewParams.URLQuery>)
- [func \(r \*VaultCredentialNewParams\) UnmarshalJSON\(data \[\]byte\) error](<#VaultCredentialNewParams.UnmarshalJSON>)
- [type VaultCredentialService](<#VaultCredentialService>)
- [func NewVaultCredentialService\(opts ...option.RequestOption\) VaultCredentialService](<#NewVaultCredentialService>)
@@ -890,6 +898,7 @@ import "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
- [func \(r VaultListParams\) URLQuery\(\) \(url.Values, error\)](<#VaultListParams.URLQuery>)
- [type VaultNewParams](<#VaultNewParams>)
- [func \(r VaultNewParams\) MarshalJSON\(\) \(\[\]byte, error\)](<#VaultNewParams.MarshalJSON>)
+ - [func \(r VaultNewParams\) URLQuery\(\) \(url.Values, error\)](<#VaultNewParams.URLQuery>)
- [func \(r \*VaultNewParams\) UnmarshalJSON\(data \[\]byte\) error](<#VaultNewParams.UnmarshalJSON>)
- [type VaultService](<#VaultService>)
- [func NewVaultService\(opts ...option.RequestOption\) VaultService](<#NewVaultService>)
@@ -956,6 +965,15 @@ func String(v string) param.Opt[string]
+
+## func [WithIdentityID]()
+
+```go
+func WithIdentityID(identityID string) option.RequestOption
+```
+
+WithIdentityID selects identity ownership for Forward resources using a PAT. Omit this option when using a SAT; its identity is inferred by the server. Use it on retrieval, deletion, archive, and download methods without params.
+
## type [Batch]()
@@ -1821,6 +1839,11 @@ func (r *ChannelNewParams) UnmarshalJSON(data []byte) error
```go
type ChannelPairing struct {
+ ScopeType string `json:"scope_type"`
+ ScopeExternalID string `json:"scope_external_id"`
+ ScopeDisplayName string `json:"scope_display_name"`
+ CreatedAt string `json:"created_at"`
+ UpdatedAt string `json:"updated_at"`
// Pairing ID, used when removing the pairing.
ID string `json:"id"`
// Always `channel_pairing`.
@@ -1836,14 +1859,19 @@ type ChannelPairing struct {
// Time the pairing completed.
PairedAt string `json:"paired_at"`
JSON struct {
- ID respjson.Field
- Type respjson.Field
- ChannelID respjson.Field
- IdentityID respjson.Field
- TemplateID respjson.Field
- Status respjson.Field
- PairedAt respjson.Field
- ExtraFields map[string]respjson.Field
+ ScopeType respjson.Field
+ ScopeExternalID respjson.Field
+ ScopeDisplayName respjson.Field
+ CreatedAt respjson.Field
+ UpdatedAt respjson.Field
+ ID respjson.Field
+ Type respjson.Field
+ ChannelID respjson.Field
+ IdentityID respjson.Field
+ TemplateID respjson.Field
+ Status respjson.Field
+ PairedAt respjson.Field
+ ExtraFields map[string]respjson.Field
// contains filtered or unexported fields
} `json:"-"`
}
@@ -1874,8 +1902,10 @@ func (r *ChannelPairing) UnmarshalJSON(data []byte) error
```go
type ChannelPairingNewParams struct {
- // Six-digit pairing code shown in the Channel message.
- Code string `json:"code" api:"required"`
+ // Pairing code. Supply exactly one of Code or ChannelPairingID.
+ Code string `json:"code,omitzero"`
+ // Pending pairing ID. Supply exactly one of Code or ChannelPairingID.
+ ChannelPairingID string `json:"channel_pairing_id,omitzero"`
// Forward Identity ID to bind.
IdentityID string `json:"identity_id" api:"required"`
// Forward Template ID to bind.
@@ -3441,6 +3471,8 @@ func (r *EnvironmentConfigPackages) UnmarshalJSON(data []byte) error
```go
type EnvironmentListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended); take the value from `next_page` in the previous
@@ -3470,6 +3502,8 @@ func (r EnvironmentListParams) URLQuery() (url.Values, error)
```go
type EnvironmentNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Environment name; must not be empty after trimming surrounding whitespace.
Name string `json:"name" api:"required"`
// Description.
@@ -3497,6 +3531,15 @@ func (r EnvironmentNewParams) MarshalJSON() ([]byte, error)
+
+### func \(EnvironmentNewParams\) [URLQuery]()
+
+```go
+func (r EnvironmentNewParams) URLQuery() (url.Values, error)
+```
+
+
+
### func \(\*EnvironmentNewParams\) [UnmarshalJSON]()
@@ -3596,6 +3639,8 @@ Update Environment
```go
type EnvironmentUpdateParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// New name.
Name param.Opt[string] `json:"name,omitzero"`
// New description.
@@ -3619,6 +3664,15 @@ func (r EnvironmentUpdateParams) MarshalJSON() ([]byte, error)
+
+### func \(EnvironmentUpdateParams\) [URLQuery]()
+
+```go
+func (r EnvironmentUpdateParams) URLQuery() (url.Values, error)
+```
+
+
+
### func \(\*EnvironmentUpdateParams\) [UnmarshalJSON]()
@@ -3805,6 +3859,8 @@ func (r *EventContentUnionParam) UnmarshalJSON(data []byte) error
```go
type FileListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous response;
@@ -3979,6 +4035,8 @@ Upload File
```go
type FileUploadParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Content of the file to upload. For accepted types see
// [Supported upload file types](./schemas.md).
File io.Reader `json:"file" api:"required" format:"binary"`
@@ -4008,6 +4066,15 @@ func (r FileUploadParams) MarshalMultipart() ([]byte, string, error)
+
+### func \(FileUploadParams\) [URLQuery]()
+
+```go
+func (r FileUploadParams) URLQuery() (url.Values, error)
+```
+
+
+
## type [GitHubRepository]()
@@ -10039,10 +10106,14 @@ func (r *SessionUpdateParamsConfigParam) UnmarshalJSON(data []byte) error
```go
type SessionUsage struct {
- TotalCredits float64 `json:"total_credits"`
- JSON struct {
- TotalCredits respjson.Field
- ExtraFields map[string]respjson.Field
+ TotalCredits float64 `json:"total_credits"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
+ ExtraFields map[string]respjson.Field
// contains filtered or unexported fields
} `json:"-"`
}
@@ -10202,6 +10273,8 @@ func (r *SkillBindingParam) UnmarshalJSON(data []byte) error
```go
type SkillGetParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// ⚠️ **Deprecated**: when `true`, the response also carries `content` and
// `content_encoding` (a base64 zip). Responses carry the `Deprecation: true` header
// when it is used. Use [Download Skill version content](./Versions/download.md)
@@ -10227,6 +10300,8 @@ func (r SkillGetParams) URLQuery() (url.Values, error)
```go
type SkillListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous response;
@@ -10264,6 +10339,8 @@ func (r SkillListParams) URLQuery() (url.Values, error)
```go
type SkillNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Recommended upload field, which may **repeat** several times. Two shapes are
// supported: (1) a single `.zip` archive; (2) a bare file tree — each part uploads
// one file and carries its relative path in `filename` (such as
@@ -10304,6 +10381,15 @@ func (r SkillNewParams) MarshalMultipart() ([]byte, string, error)
+
+### func \(SkillNewParams\) [URLQuery]()
+
+```go
+func (r SkillNewParams) URLQuery() (url.Values, error)
+```
+
+
+
## type [SkillOverride]()
@@ -10456,6 +10542,8 @@ Update Skill
```go
type SkillUpdateParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// New description.
Description param.Opt[string] `json:"description,omitzero"`
// New content (the contents of a zip archive). Neither the archive itself nor its
@@ -10490,6 +10578,15 @@ func (r SkillUpdateParams) MarshalJSON() ([]byte, error)
+
+### func \(SkillUpdateParams\) [URLQuery]()
+
+```go
+func (r SkillUpdateParams) URLQuery() (url.Values, error)
+```
+
+
+
### func \(\*SkillUpdateParams\) [UnmarshalJSON]()
@@ -10560,6 +10657,8 @@ func (r *SkillVersion) UnmarshalJSON(data []byte) error
```go
type SkillVersionListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100, default 20.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Cursor for paging forward; take the value from `next_page` in the previous
@@ -10585,6 +10684,8 @@ func (r SkillVersionListParams) URLQuery() (url.Values, error)
```go
type SkillVersionNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Upload field, which may appear **multiple** times. Two shapes are supported: • a
// single `.zip` archive; • a bare file tree, where every part uploads one file and
// `filename` carries its relative path (e.g. `customer-reply/SKILL.md`,
@@ -10604,6 +10705,15 @@ func (r SkillVersionNewParams) MarshalMultipart() ([]byte, string, error)
+
+### func \(SkillVersionNewParams\) [URLQuery]()
+
+```go
+func (r SkillVersionNewParams) URLQuery() (url.Values, error)
+```
+
+
+
## type [SkillVersionService]()
@@ -11677,6 +11787,8 @@ func (r *VaultCredentialAuth) UnmarshalJSON(data []byte) error
```go
type VaultCredentialListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous response;
@@ -11708,6 +11820,8 @@ func (r VaultCredentialListParams) URLQuery() (url.Values, error)
```go
type VaultCredentialNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Credential authentication material; supports `static_bearer` and `mcp_oauth`. The
// response only returns redacted, non-secret fields.
Auth map[string]any `json:"auth" api:"required"`
@@ -11733,6 +11847,15 @@ func (r VaultCredentialNewParams) MarshalJSON() ([]byte, error)
+
+### func \(VaultCredentialNewParams\) [URLQuery]()
+
+```go
+func (r VaultCredentialNewParams) URLQuery() (url.Values, error)
+```
+
+
+
### func \(\*VaultCredentialNewParams\) [UnmarshalJSON]()
@@ -11896,6 +12019,8 @@ func (r *VaultCredentialUpdateParams) UnmarshalJSON(data []byte) error
```go
type VaultListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous
@@ -11927,6 +12052,8 @@ func (r VaultListParams) URLQuery() (url.Values, error)
```go
type VaultNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Vault display name.
DisplayName string `json:"display_name" api:"required"`
// Metadata object; `created_by` is reserved and cannot be supplied (supplying it
@@ -11948,6 +12075,15 @@ func (r VaultNewParams) MarshalJSON() ([]byte, error)
+
+### func \(VaultNewParams\) [URLQuery]()
+
+```go
+func (r VaultNewParams) URLQuery() (url.Values, error)
+```
+
+
+
### func \(\*VaultNewParams\) [UnmarshalJSON]()
@@ -18258,8 +18394,8 @@ type DeploymentNewParams struct {
Name string `json:"name" api:"required"`
// Description of what the deployment does.
Description param.Opt[string] `json:"description,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up
// to 512 chars.
@@ -18832,8 +18968,8 @@ type DeploymentUpdateParams struct {
// version, or an `agent` object with both id and version specified. Omit to
// preserve. Cannot be cleared.
Agent DeploymentUpdateParamsAgentUnion `json:"agent,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Initial events. Full replacement. Omit to preserve. Cannot be cleared. At least
// 1, maximum 50.
@@ -23202,6 +23338,8 @@ Indicates that context compaction \(summarization\) occurred during the session.
```go
type ManagedAgentsAgentThreadContextCompactedEvent struct {
+ // Credits for this compaction call, already included in Session totals.
+ Usage ManagedAgentsSpanModelUsage `json:"usage"`
// Unique identifier for this event.
ID string `json:"id" api:"required"`
// A timestamp in RFC 3339 format
@@ -23210,6 +23348,7 @@ type ManagedAgentsAgentThreadContextCompactedEvent struct {
Type ManagedAgentsAgentThreadContextCompactedEventType `json:"type" api:"required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
+ Usage respjson.Field
ID respjson.Field
ProcessedAt respjson.Field
Type respjson.Field
@@ -26101,19 +26240,19 @@ const (
## type [ManagedAgentsBudgetLimit]()
-A hard spend ceiling. The session stops issuing new model requests once the tracked list cost reaches \`max\_list\_cost\`.
+A total model and sandbox credit limit. Once reached, new user messages are rejected; an already running turn continues to completion.
```go
type ManagedAgentsBudgetLimit struct {
- // A monetary amount in a specific currency.
- MaxListCost MonetaryAmount `json:"max_list_cost" api:"required"`
+ // Positive decimal Credits, with at most 12 integer and 2 fractional digits.
+ MaxCreditCost string `json:"max_credit_cost" api:"required"`
// Any of "limit".
Type ManagedAgentsBudgetLimitType `json:"type" api:"required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- MaxListCost respjson.Field
- Type respjson.Field
- ExtraFields map[string]respjson.Field
+ MaxCreditCost respjson.Field
+ Type respjson.Field
+ ExtraFields map[string]respjson.Field
// contains filtered or unexported fields
} `json:"-"`
}
@@ -26151,14 +26290,14 @@ func (r *ManagedAgentsBudgetLimit) UnmarshalJSON(data []byte) error
## type [ManagedAgentsBudgetLimitParam]()
-A hard spend ceiling. The session stops issuing new model requests once the tracked list cost reaches \`max\_list\_cost\`.
+A total model and sandbox credit limit. Once reached, new user messages are rejected; an already running turn continues to completion.
-The properties MaxListCost, Type are required.
+The properties MaxCreditCost, Type are required.
```go
type ManagedAgentsBudgetLimitParam struct {
- // A monetary amount in a specific currency.
- MaxListCost MonetaryAmountParam `json:"max_list_cost,omitzero" api:"required"`
+ // Positive decimal Credits, with at most 12 integer and 2 fractional digits.
+ MaxCreditCost string `json:"max_credit_cost" api:"required"`
// Any of "limit".
Type ManagedAgentsBudgetLimitType `json:"type,omitzero" api:"required"`
// contains filtered or unexported fields
@@ -27667,8 +27806,8 @@ type ManagedAgentsDeployment struct {
// Vault IDs supplying stored credentials for sessions created from this
// deployment.
VaultIDs []string `json:"vault_ids" api:"required"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
@@ -38150,9 +38289,9 @@ type ManagedAgentsSession struct {
Agent ManagedAgentsSessionAgent `json:"agent" api:"required"`
// A timestamp in RFC 3339 format
ArchivedAt time.Time `json:"archived_at" api:"required" format:"date-time"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
- Budget ManagedAgentsBudgetLimit `json:"budget" api:"required"`
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
+ Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// A timestamp in RFC 3339 format
CreatedAt time.Time `json:"created_at" api:"required" format:"date-time"`
EnvironmentID string `json:"environment_id" api:"required"`
@@ -38172,8 +38311,9 @@ type ManagedAgentsSession struct {
Type ManagedAgentsSessionType `json:"type" api:"required"`
// A timestamp in RFC 3339 format
UpdatedAt time.Time `json:"updated_at" api:"required" format:"date-time"`
- // Cumulative token usage for a session across all turns.
- Usage ManagedAgentsSessionUsage `json:"usage" api:"required"`
+ // Cumulative Credits snapshot. Replace previous values; do not sum snapshots.
+ // Legacy token and list-cost fields remain available for older responses.
+ Usage ManagedAgentsSessionUsage `json:"usage"`
// Vault IDs attached to the session at creation. Empty when no vaults were
// supplied.
VaultIDs []string `json:"vault_ids" api:"required"`
@@ -39255,7 +39395,10 @@ Use the methods beginning with 'As' to cast the union to one of its variants.
```go
type ManagedAgentsSessionEventUnion struct {
- ID string `json:"id"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
+ ID string `json:"id"`
// This field is a union of [[]ManagedAgentsUserMessageEventContentUnion],
// [[]ManagedAgentsUserCustomToolResultEventContentUnion],
// [[]ManagedAgentsAgentMessageEventContentUnion],
@@ -39340,6 +39483,9 @@ type ManagedAgentsSessionEventUnion struct {
// This field is from variant [ManagedAgentsSessionUpdatedEvent].
Title string `json:"title"`
JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
ID respjson.Field
Content respjson.Field
Type respjson.Field
@@ -39869,6 +40015,7 @@ For type safety it is recommended to directly use a variant of the [ManagedAgent
```go
type ManagedAgentsSessionEventUnionUsage struct {
+ Credits float64 `json:"credits"`
// This field is from variant [ManagedAgentsSpanModelUsage].
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
@@ -39885,6 +40032,7 @@ type ManagedAgentsSessionEventUnionUsage struct {
// This field is from variant [ManagedAgentsSessionUsageSnapshot].
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use"`
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
@@ -42154,8 +42302,8 @@ type ManagedAgentsSessionUpdatedEvent struct {
// Resolved `agent` definition for a `session`. Snapshot of the `agent` at
// `session` creation time.
Agent ManagedAgentsSessionAgent `json:"agent" api:"nullable"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// The session's full metadata bag after the update. Present when the update set
// non-empty metadata; absent when metadata was unchanged or cleared to empty.
@@ -42215,10 +42363,13 @@ const (
## type [ManagedAgentsSessionUsage]()
-Cumulative token usage for a session across all turns.
+Cumulative Credits snapshot. Replace previous values; do not sum snapshots. Legacy token and list\-cost fields remain available for older responses.
```go
type ManagedAgentsSessionUsage struct {
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
// Cumulative time in seconds during which the session had at least one thread in
// running status. Overlapping activity from concurrent threads is counted once,
// unlike `stats.active_seconds`, which sums each thread's own active time. This is
@@ -42238,14 +42389,17 @@ type ManagedAgentsSessionUsage struct {
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- ActiveSeconds respjson.Field
- CacheCreation respjson.Field
- CacheReadInputTokens respjson.Field
- InputTokens respjson.Field
- ListCost respjson.Field
- OutputTokens respjson.Field
- ServerToolUse respjson.Field
- ExtraFields map[string]respjson.Field
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
+ ActiveSeconds respjson.Field
+ CacheCreation respjson.Field
+ CacheReadInputTokens respjson.Field
+ InputTokens respjson.Field
+ ListCost respjson.Field
+ OutputTokens respjson.Field
+ ServerToolUse respjson.Field
+ ExtraFields map[string]respjson.Field
// contains filtered or unexported fields
} `json:"-"`
}
@@ -42272,10 +42426,13 @@ func (r *ManagedAgentsSessionUsage) UnmarshalJSON(data []byte) error
## type [ManagedAgentsSessionUsageEvent]()
-Periodic snapshot of the session's cumulative usage and tracked list cost.
+Cumulative Credits emitted before session.status\_idle when usage is available. Credits are top\-level fields; Usage and Budget are legacy response fields.
```go
type ManagedAgentsSessionUsageEvent struct {
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
// Unique identifier for this event.
ID string `json:"id" api:"required"`
// A timestamp in RFC 3339 format
@@ -42283,18 +42440,21 @@ type ManagedAgentsSessionUsageEvent struct {
// Any of "session.usage".
Type ManagedAgentsSessionUsageEventType `json:"type" api:"required"`
// Point-in-time snapshot of a session's cumulative usage.
- Usage ManagedAgentsSessionUsageSnapshot `json:"usage" api:"required"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ Usage ManagedAgentsSessionUsageSnapshot `json:"usage"`
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- ID respjson.Field
- ProcessedAt respjson.Field
- Type respjson.Field
- Usage respjson.Field
- Budget respjson.Field
- ExtraFields map[string]respjson.Field
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
+ ID respjson.Field
+ ProcessedAt respjson.Field
+ Type respjson.Field
+ Usage respjson.Field
+ Budget respjson.Field
+ ExtraFields map[string]respjson.Field
// contains filtered or unexported fields
} `json:"-"`
}
@@ -42591,8 +42751,8 @@ type ManagedAgentsSpanModelRequestEndEvent struct {
IsError bool `json:"is_error" api:"required"`
// The id of the corresponding `span.model_request_start` event.
ModelRequestStartID string `json:"model_request_start_id" api:"required"`
- // Token usage for a single model request.
- ModelUsage ManagedAgentsSpanModelUsage `json:"model_usage" api:"required"`
+ // Credits for a single model request; legacy token fields may appear in older responses.
+ ModelUsage ManagedAgentsSpanModelUsage `json:"model_usage"`
// A timestamp in RFC 3339 format
ProcessedAt time.Time `json:"processed_at" api:"required" format:"date-time"`
// Any of "span.model_request_end".
@@ -42708,18 +42868,19 @@ const (
## type [ManagedAgentsSpanModelUsage]()
-Token usage for a single model request.
+Credits for a single model request; legacy token fields may appear in older responses.
```go
type ManagedAgentsSpanModelUsage struct {
+ Credits float64 `json:"credits"`
// Tokens used to create prompt cache in this request.
- CacheCreationInputTokens int64 `json:"cache_creation_input_tokens" api:"required"`
+ CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
// Tokens read from prompt cache in this request.
- CacheReadInputTokens int64 `json:"cache_read_input_tokens" api:"required"`
+ CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
// Input tokens consumed by this request.
- InputTokens int64 `json:"input_tokens" api:"required"`
+ InputTokens int64 `json:"input_tokens"`
// Output tokens generated by this request.
- OutputTokens int64 `json:"output_tokens" api:"required"`
+ OutputTokens int64 `json:"output_tokens"`
// Inference speed mode. `fast` provides significantly faster output token
// generation at premium pricing. Not all models support `fast`; invalid
// combinations are rejected at create time.
@@ -42728,6 +42889,7 @@ type ManagedAgentsSpanModelUsage struct {
Speed ManagedAgentsSpanModelUsageSpeed `json:"speed" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
@@ -42806,7 +42968,7 @@ type ManagedAgentsSpanOutcomeEvaluationEndEvent struct {
Result string `json:"result" api:"required"`
// Any of "span.outcome_evaluation_end".
Type ManagedAgentsSpanOutcomeEvaluationEndEventType `json:"type" api:"required"`
- // Token usage for a single model request.
+ // Credits for a single model request; legacy token fields may appear in older responses.
Usage ManagedAgentsSpanModelUsage `json:"usage" api:"required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
@@ -43300,7 +43462,10 @@ Use the methods beginning with 'As' to cast the union to one of its variants.
```go
type ManagedAgentsStreamSessionEventsUnion struct {
- ID string `json:"id"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
+ ID string `json:"id"`
// This field is a union of [[]ManagedAgentsUserMessageEventContentUnion],
// [[]ManagedAgentsUserCustomToolResultEventContentUnion],
// [[]ManagedAgentsAgentMessageEventContentUnion],
@@ -43391,6 +43556,9 @@ type ManagedAgentsStreamSessionEventsUnion struct {
// This field is from variant [ManagedAgentsDeltaEvent].
EventID string `json:"event_id"`
JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
ID respjson.Field
Content respjson.Field
Type respjson.Field
@@ -43943,6 +44111,7 @@ For type safety it is recommended to directly use a variant of the [ManagedAgent
```go
type ManagedAgentsStreamSessionEventsUnionUsage struct {
+ Credits float64 `json:"credits"`
// This field is from variant [ManagedAgentsSpanModelUsage].
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
@@ -43959,6 +44128,7 @@ type ManagedAgentsStreamSessionEventsUnionUsage struct {
// This field is from variant [ManagedAgentsSessionUsageSnapshot].
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use"`
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
@@ -43993,7 +44163,10 @@ Use the methods beginning with 'As' to cast the union to one of its variants.
```go
type ManagedAgentsStreamSessionThreadEventsUnion struct {
- ID string `json:"id"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
+ ID string `json:"id"`
// This field is a union of [[]ManagedAgentsUserMessageEventContentUnion],
// [[]ManagedAgentsUserCustomToolResultEventContentUnion],
// [[]ManagedAgentsAgentMessageEventContentUnion],
@@ -44084,6 +44257,9 @@ type ManagedAgentsStreamSessionThreadEventsUnion struct {
// This field is from variant [ManagedAgentsDeltaEvent].
EventID string `json:"event_id"`
JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
ID respjson.Field
Content respjson.Field
Type respjson.Field
@@ -44636,6 +44812,7 @@ For type safety it is recommended to directly use a variant of the [ManagedAgent
```go
type ManagedAgentsStreamSessionThreadEventsUnionUsage struct {
+ Credits float64 `json:"credits"`
// This field is from variant [ManagedAgentsSpanModelUsage].
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
@@ -44652,6 +44829,7 @@ type ManagedAgentsStreamSessionThreadEventsUnionUsage struct {
// This field is from variant [ManagedAgentsSessionUsageSnapshot].
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use"`
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
@@ -52180,8 +52358,8 @@ type SessionNewParams struct {
EnvironmentID string `json:"environment_id" api:"required"`
// Human-readable session title.
Title param.Opt[string] `json:"title,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Initial events to send to the `session` at creation, processed in order.
// Supports `user.message` and `user.define_outcome` events. Maximum 50 events.
@@ -53293,8 +53471,8 @@ type SessionUpdateParams struct {
// updatable. Full replacement: the provided array becomes the new value. To
// preserve existing entries, GET the session, modify the array, and POST it back.
Agent ManagedAgentsSessionAgentUpdateParam `json:"agent,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests
// setting this field are rejected. Reserved for future use.
diff --git a/forward/channelpairing.go b/forward/channelpairing.go
index b25d9a6..9ff8632 100644
--- a/forward/channelpairing.go
+++ b/forward/channelpairing.go
@@ -35,8 +35,10 @@ func (r *ChannelPairingService) New(ctx context.Context, params ChannelPairingNe
}
type ChannelPairingNewParams struct {
- // Six-digit pairing code shown in the Channel message.
- Code string `json:"code" api:"required"`
+ // Pairing code. Supply exactly one of Code or ChannelPairingID.
+ Code string `json:"code,omitzero"`
+ // Pending pairing ID. Supply exactly one of Code or ChannelPairingID.
+ ChannelPairingID string `json:"channel_pairing_id,omitzero"`
// Forward Identity ID to bind.
IdentityID string `json:"identity_id" api:"required"`
// Forward Template ID to bind.
@@ -86,6 +88,11 @@ func (r *DeletedChannelPairing) UnmarshalJSON(data []byte) error {
}
type ChannelPairing struct {
+ ScopeType string `json:"scope_type"`
+ ScopeExternalID string `json:"scope_external_id"`
+ ScopeDisplayName string `json:"scope_display_name"`
+ CreatedAt string `json:"created_at"`
+ UpdatedAt string `json:"updated_at"`
// Pairing ID, used when removing the pairing.
ID string `json:"id"`
// Always `channel_pairing`.
@@ -101,15 +108,20 @@ type ChannelPairing struct {
// Time the pairing completed.
PairedAt string `json:"paired_at"`
JSON struct {
- ID respjson.Field
- Type respjson.Field
- ChannelID respjson.Field
- IdentityID respjson.Field
- TemplateID respjson.Field
- Status respjson.Field
- PairedAt respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ ScopeType respjson.Field
+ ScopeExternalID respjson.Field
+ ScopeDisplayName respjson.Field
+ CreatedAt respjson.Field
+ UpdatedAt respjson.Field
+ ID respjson.Field
+ Type respjson.Field
+ ChannelID respjson.Field
+ IdentityID respjson.Field
+ TemplateID respjson.Field
+ Status respjson.Field
+ PairedAt respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
diff --git a/forward/environment.go b/forward/environment.go
index d5b451f..541945c 100644
--- a/forward/environment.go
+++ b/forward/environment.go
@@ -48,6 +48,8 @@ func (r *EnvironmentService) ListAutoPaging(ctx context.Context, params Environm
}
type EnvironmentListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended); take the value from `next_page` in the previous
@@ -76,6 +78,8 @@ func (r *EnvironmentService) New(ctx context.Context, params EnvironmentNewParam
}
type EnvironmentNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Environment name; must not be empty after trimming surrounding whitespace.
Name string `json:"name" api:"required"`
// Description.
@@ -126,6 +130,8 @@ func (r *EnvironmentService) Update(ctx context.Context, id string, params Envir
}
type EnvironmentUpdateParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// New name.
Name param.Opt[string] `json:"name,omitzero"`
// New description.
@@ -239,3 +245,11 @@ func (r EnvironmentConfigPackages) RawJSON() string { return r.JSON.raw }
func (r *EnvironmentConfigPackages) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+
+func (r EnvironmentNewParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
+
+func (r EnvironmentUpdateParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
diff --git a/forward/file.go b/forward/file.go
index 58e9caa..f33ca32 100644
--- a/forward/file.go
+++ b/forward/file.go
@@ -49,6 +49,8 @@ func (r *FileService) ListAutoPaging(ctx context.Context, params FileListParams,
}
type FileListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous response;
@@ -83,6 +85,8 @@ func (r *FileService) Upload(ctx context.Context, params FileUploadParams, opts
}
type FileUploadParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Content of the file to upload. For accepted types see
// [Supported upload file types](./schemas.md).
File io.Reader `json:"file" api:"required" format:"binary"`
@@ -183,3 +187,7 @@ type FileMetadata struct {
func (r FileMetadata) RawJSON() string { return r.JSON.raw }
func (r *FileMetadata) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) }
+
+func (r FileUploadParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
diff --git a/forward/identity_pairing_contract_test.go b/forward/identity_pairing_contract_test.go
new file mode 100644
index 0000000..1c2cac8
--- /dev/null
+++ b/forward/identity_pairing_contract_test.go
@@ -0,0 +1,245 @@
+package forward_test
+
+import (
+ "context"
+ "encoding/json"
+ "io"
+ "net/http"
+ "strings"
+ "testing"
+
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/option"
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/param"
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
+)
+
+// Official Forward ownership contracts (2026-10-20), independent of inventories.
+func TestIdentityOwnershipContracts(t *testing.T) {
+ ctx := context.Background()
+ for _, scope := range []struct {
+ name string
+ identity param.Opt[string]
+ }{{"PAT identity", forward.String("idn_ /?&")}, {"explicit empty", forward.String("")}, {"omitted", param.Opt[string]{}}} {
+ t.Run(scope.name, func(t *testing.T) {
+ identity := scope.identity
+ var opts []option.RequestOption
+ if identity.Valid() {
+ opts = append(opts, forward.WithIdentityID(identity.Value))
+ }
+ cases := []struct {
+ method, path string
+ call func(forward.Client) error
+ }{
+ {"GET", "environments", func(c forward.Client) error {
+ _, err := c.Environments.List(ctx, forward.EnvironmentListParams{IdentityID: identity})
+ return err
+ }},
+ {"POST", "environments", func(c forward.Client) error {
+ _, err := c.Environments.New(ctx, forward.EnvironmentNewParams{Name: "env", IdentityID: identity})
+ return err
+ }},
+ {"GET", "environments/env", func(c forward.Client) error { _, err := c.Environments.Get(ctx, "env", opts...); return err }},
+ {"POST", "environments/env", func(c forward.Client) error {
+ _, err := c.Environments.Update(ctx, "env", forward.EnvironmentUpdateParams{Name: forward.String("renamed"), IdentityID: identity})
+ return err
+ }},
+ {"POST", "environments/env/archive", func(c forward.Client) error { _, err := c.Environments.Archive(ctx, "env", opts...); return err }},
+ {"DELETE", "environments/env", func(c forward.Client) error { return c.Environments.Delete(ctx, "env", opts...) }},
+ {"GET", "skills", func(c forward.Client) error {
+ _, err := c.Skills.List(ctx, forward.SkillListParams{IdentityID: identity})
+ return err
+ }},
+ {"POST", "skills", func(c forward.Client) error {
+ _, err := c.Skills.New(ctx, forward.SkillNewParams{Files: []io.Reader{strings.NewReader("# skill")}, IdentityID: identity})
+ return err
+ }},
+ {"GET", "skills/skill", func(c forward.Client) error {
+ _, err := c.Skills.Get(ctx, "skill", forward.SkillGetParams{IdentityID: identity})
+ return err
+ }},
+ {"PUT", "skills/skill", func(c forward.Client) error {
+ _, err := c.Skills.Update(ctx, "skill", forward.SkillUpdateParams{Description: forward.String("desc"), IdentityID: identity})
+ return err
+ }},
+ {"DELETE", "skills/skill", func(c forward.Client) error { return c.Skills.Delete(ctx, "skill", opts...) }},
+ {"GET", "skills/skill/versions", func(c forward.Client) error {
+ _, err := c.Skills.Versions.List(ctx, "skill", forward.SkillVersionListParams{IdentityID: identity})
+ return err
+ }},
+ {"POST", "skills/skill/versions", func(c forward.Client) error {
+ _, err := c.Skills.Versions.New(ctx, "skill", forward.SkillVersionNewParams{Files: []io.Reader{strings.NewReader("# skill")}, IdentityID: identity})
+ return err
+ }},
+ {"GET", "skills/skill/versions/1", func(c forward.Client) error { _, err := c.Skills.Versions.Get(ctx, "skill", "1", opts...); return err }},
+ {"DELETE", "skills/skill/versions/1", func(c forward.Client) error {
+ _, err := c.Skills.Versions.Delete(ctx, "skill", "1", opts...)
+ return err
+ }},
+ {"GET", "skills/skill/versions/1/content", func(c forward.Client) error {
+ res, err := c.Skills.Versions.Download(ctx, "skill", "1", opts...)
+ if err == nil {
+ res.Body.Close()
+ }
+ return err
+ }},
+ {"GET", "vaults", func(c forward.Client) error {
+ _, err := c.Vaults.List(ctx, forward.VaultListParams{IdentityID: identity})
+ return err
+ }},
+ {"POST", "vaults", func(c forward.Client) error {
+ _, err := c.Vaults.New(ctx, forward.VaultNewParams{DisplayName: "vault", IdentityID: identity})
+ return err
+ }},
+ {"GET", "vaults/vault", func(c forward.Client) error { _, err := c.Vaults.Get(ctx, "vault", opts...); return err }},
+ {"DELETE", "vaults/vault", func(c forward.Client) error { return c.Vaults.Delete(ctx, "vault", opts...) }},
+ {"GET", "vaults/vault/credentials", func(c forward.Client) error {
+ _, err := c.Vaults.Credentials.List(ctx, "vault", forward.VaultCredentialListParams{IdentityID: identity})
+ return err
+ }},
+ {"POST", "vaults/vault/credentials", func(c forward.Client) error {
+ _, err := c.Vaults.Credentials.New(ctx, "vault", forward.VaultCredentialNewParams{Auth: map[string]any{"type": "static_bearer", "token": "secret"}, IdentityID: identity})
+ return err
+ }},
+ {"GET", "vaults/vault/credentials/cred", func(c forward.Client) error {
+ _, err := c.Vaults.Credentials.Get(ctx, "vault", "cred", opts...)
+ return err
+ }},
+ {"POST", "vaults/vault/credentials/cred", func(c forward.Client) error {
+ _, err := c.Vaults.Credentials.Update(ctx, "vault", "cred", forward.VaultCredentialUpdateParams{Metadata: map[string]any{"note": "rotation"}, IdentityID: identity})
+ return err
+ }},
+ {"DELETE", "vaults/vault/credentials/cred", func(c forward.Client) error { return c.Vaults.Credentials.Delete(ctx, "vault", "cred", opts...) }},
+ {"GET", "files", func(c forward.Client) error {
+ _, err := c.Files.List(ctx, forward.FileListParams{IdentityID: identity})
+ return err
+ }},
+ {"POST", "files", func(c forward.Client) error {
+ _, err := c.Files.Upload(ctx, forward.FileUploadParams{File: strings.NewReader("content"), IdentityID: identity})
+ return err
+ }},
+ {"GET", "files/file", func(c forward.Client) error { _, err := c.Files.GetMetadata(ctx, "file", opts...); return err }},
+ {"DELETE", "files/file", func(c forward.Client) error { return c.Files.Delete(ctx, "file", opts...) }},
+ {"GET", "files/file/content", func(c forward.Client) error {
+ res, err := c.Files.Download(ctx, "file", opts...)
+ if err == nil {
+ res.Body.Close()
+ }
+ return err
+ }},
+ }
+ for _, tc := range cases {
+ t.Run(tc.method+" "+tc.path, func(t *testing.T) {
+ calls := 0
+ c := testClient(func(r *http.Request) (*http.Response, error) {
+ if r.URL.Host == "storage.test" {
+ if r.URL.RawQuery != "signature=ok" || r.Header.Get("Authorization") != "" {
+ t.Fatal("scope or auth leaked to storage")
+ }
+ return reply(r, 200, "content"), nil
+ }
+ calls++
+ if r.Method != tc.method || r.URL.Path != "/api/v1/forward/"+tc.path {
+ t.Fatalf("route: %s %s", r.Method, r.URL)
+ }
+ query := r.URL.Query()
+ if query.Has("identity_id") != identity.Valid() || query.Get("identity_id") != identity.Value {
+ t.Fatalf("scope: %s", r.URL)
+ }
+ if len(query) > 1 {
+ t.Fatalf("body fields leaked to query: %s", r.URL)
+ }
+ if r.Body != nil {
+ body, _ := io.ReadAll(r.Body)
+ if strings.Contains(string(body), "identity_id") {
+ t.Fatalf("identity leaked into body: %s", body)
+ }
+ }
+ if tc.path == "files/file/content" {
+ return reply(r, 200, `{"url":"https://storage.test/content?signature=ok"}`), nil
+ }
+ return reply(r, 200, `{"id":"resource","data":[],"has_more":false}`), nil
+ })
+ if err := tc.call(c); err != nil {
+ t.Fatal(err)
+ }
+ if calls != 1 {
+ t.Fatalf("API calls: %d", calls)
+ }
+ })
+ }
+ })
+ }
+}
+
+func TestPairChannelByCodeOrID(t *testing.T) {
+ for _, byID := range []bool{false, true} {
+ t.Run(map[bool]string{false: "code", true: "ID"}[byID], func(t *testing.T) {
+ params := forward.ChannelPairingNewParams{IdentityID: "idn_one", TemplateID: "tmpl_one", IdempotencyKey: forward.String("pair-once")}
+ expected := map[string]any{"identity_id": "idn_one", "template_id": "tmpl_one"}
+ if byID {
+ params.ChannelPairingID = "pair_one"
+ expected["channel_pairing_id"] = "pair_one"
+ } else {
+ params.Code = "K7MP92"
+ expected["code"] = "K7MP92"
+ }
+ c := testClient(func(r *http.Request) (*http.Response, error) {
+ if r.URL.RawQuery != "" || r.Header.Get("Idempotency-Key") != "pair-once" {
+ t.Fatal("pairing query/header")
+ }
+ var body map[string]any
+ if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+ t.Fatal(err)
+ }
+ if len(body) != len(expected) {
+ t.Fatalf("unexpected body: %+v", body)
+ }
+ for k, v := range expected {
+ if body[k] != v {
+ t.Fatalf("body: %+v", body)
+ }
+ }
+ return reply(r, 200, `{"id": "pair_one", "type": "channel_pairing", "channel_id": "channel_one", "scope_type": "room", "scope_external_id": "room_one", "scope_display_name": "Team", "identity_id": "idn_one", "template_id": "tmpl_one", "status": "active", "paired_at": "2026-10-20T01:00:00Z"}`), nil
+ })
+ res, err := c.ChannelPairings.New(context.Background(), params)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.ScopeType != "room" || res.ScopeExternalID != "room_one" || res.ScopeDisplayName != "Team" {
+ t.Fatalf("pairing=%+v", res)
+ }
+ })
+ }
+}
+
+func TestIdentityOwnershipPagingAndOverride(t *testing.T) {
+ calls := 0
+ c := testClient(func(r *http.Request) (*http.Response, error) {
+ if r.URL.Query().Get("identity_id") != "idn_override" {
+ t.Fatalf("lost scope: %s", r.URL)
+ }
+ calls++
+ if calls == 1 {
+ return reply(r, 200, `{"data":[{"id":"env_one"}],"has_more":true,"next_page":"next"}`), nil
+ }
+ if calls == 2 && r.URL.Query().Get("page") != "next" {
+ t.Fatalf("lost cursor: %s", r.URL)
+ }
+ return reply(r, 200, `{"data":[{"id":"env_two"}],"has_more":false}`), nil
+ })
+ pager := c.Environments.ListAutoPaging(context.Background(), forward.EnvironmentListParams{IdentityID: forward.String("idn_typed")}, forward.WithIdentityID("idn_override"))
+ count := 0
+ for pager.Next() {
+ count++
+ }
+ if err := pager.Err(); err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Fatalf("items=%d", count)
+ }
+ _, err := c.Environments.New(context.Background(), forward.EnvironmentNewParams{Name: "env", IdentityID: forward.String("idn_typed")}, forward.WithIdentityID("idn_override"))
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/forward/params.go b/forward/params.go
index e28a615..fee34ca 100644
--- a/forward/params.go
+++ b/forward/params.go
@@ -10,6 +10,7 @@ import (
"github.com/QoderAI/qoder-cloud-agents-sdk-go/convention"
"github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/apiform"
"github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/apijson"
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/option"
"github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/param"
"github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/respjson"
"github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/thirdparty/gjson"
@@ -24,6 +25,13 @@ func Int(v int64) param.Opt[int64] { return param.NewOpt(v) }
func Bool(v bool) param.Opt[bool] { return param.NewOpt(v) }
func Float(v float64) param.Opt[float64] { return param.NewOpt(v) }
+// WithIdentityID selects identity ownership for Forward resources using a PAT.
+// Omit this option when using a SAT; its identity is inferred by the server.
+// Use it on retrieval, deletion, archive, and download methods without params.
+func WithIdentityID(identityID string) option.RequestOption {
+ return option.WithQuery("identity_id", identityID)
+}
+
// ModelConfig preserves either a model ID string or an object from the API.
// ID is populated for both shapes; RawJSON retains the original representation.
type ModelConfig struct {
diff --git a/forward/session.go b/forward/session.go
index acb23ce..32cb425 100644
--- a/forward/session.go
+++ b/forward/session.go
@@ -287,11 +287,15 @@ func (r Session) RawJSON() string { return r.JSON.raw }
func (r *Session) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) }
type SessionUsage struct {
- TotalCredits float64 `json:"total_credits"`
- JSON struct {
- TotalCredits respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ TotalCredits float64 `json:"total_credits"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
diff --git a/forward/skill.go b/forward/skill.go
index 8d00361..2848a45 100644
--- a/forward/skill.go
+++ b/forward/skill.go
@@ -50,6 +50,8 @@ func (r *SkillService) ListAutoPaging(ctx context.Context, params SkillListParam
}
type SkillListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous response;
@@ -86,6 +88,8 @@ func (r *SkillService) New(ctx context.Context, params SkillNewParams, opts ...o
}
type SkillNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Recommended upload field, which may **repeat** several times. Two shapes are
// supported: (1) a single `.zip` archive; (2) a bare file tree — each part uploads
// one file and carries its relative path in `filename` (such as
@@ -133,6 +137,8 @@ func (r *SkillService) Get(ctx context.Context, id string, params SkillGetParams
}
type SkillGetParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// ⚠️ **Deprecated**: when `true`, the response also carries `content` and
// `content_encoding` (a base64 zip). Responses carry the `Deprecation: true` header
// when it is used. Use [Download Skill version content](./Versions/download.md)
@@ -158,6 +164,8 @@ func (r *SkillService) Update(ctx context.Context, id string, params SkillUpdate
}
type SkillUpdateParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// New description.
Description param.Opt[string] `json:"description,omitzero"`
// New content (the contents of a zip archive). Neither the archive itself nor its
@@ -233,3 +241,11 @@ type Skill struct {
func (r Skill) RawJSON() string { return r.JSON.raw }
func (r *Skill) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) }
+
+func (r SkillNewParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
+
+func (r SkillUpdateParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
diff --git a/forward/skillversion.go b/forward/skillversion.go
index f0aedec..58a5fc9 100644
--- a/forward/skillversion.go
+++ b/forward/skillversion.go
@@ -52,6 +52,8 @@ func (r *SkillVersionService) ListAutoPaging(ctx context.Context, id string, par
}
type SkillVersionListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100, default 20.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Cursor for paging forward; take the value from `next_page` in the previous
@@ -77,6 +79,8 @@ func (r *SkillVersionService) New(ctx context.Context, id string, params SkillVe
}
type SkillVersionNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Upload field, which may appear **multiple** times. Two shapes are supported: • a
// single `.zip` archive; • a bare file tree, where every part uploads one file and
// `filename` carries its relative path (e.g. `customer-reply/SKILL.md`,
@@ -185,3 +189,7 @@ type SkillVersion struct {
func (r SkillVersion) RawJSON() string { return r.JSON.raw }
func (r *SkillVersion) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) }
+
+func (r SkillVersionNewParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
diff --git a/forward/vault.go b/forward/vault.go
index 00537f1..928948c 100644
--- a/forward/vault.go
+++ b/forward/vault.go
@@ -49,6 +49,8 @@ func (r *VaultService) ListAutoPaging(ctx context.Context, params VaultListParam
}
type VaultListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous
@@ -79,6 +81,8 @@ func (r *VaultService) New(ctx context.Context, params VaultNewParams, opts ...o
}
type VaultNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Vault display name.
DisplayName string `json:"display_name" api:"required"`
// Metadata object; `created_by` is reserved and cannot be supplied (supplying it
@@ -150,3 +154,7 @@ type Vault struct {
func (r Vault) RawJSON() string { return r.JSON.raw }
func (r *Vault) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) }
+
+func (r VaultNewParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
diff --git a/forward/vaultcredential.go b/forward/vaultcredential.go
index c9c453f..0091ec3 100644
--- a/forward/vaultcredential.go
+++ b/forward/vaultcredential.go
@@ -51,6 +51,8 @@ func (r *VaultCredentialService) ListAutoPaging(ctx context.Context, id string,
}
type VaultCredentialListParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Page size, maximum 100.
Limit param.Opt[int64] `query:"limit,omitzero" json:"-"`
// Pagination cursor (recommended), taken from `next_page` in the previous response;
@@ -84,6 +86,8 @@ func (r *VaultCredentialService) New(ctx context.Context, id string, params Vaul
}
type VaultCredentialNewParams struct {
+ // Select identity ownership with a PAT. Omit when using a SAT.
+ IdentityID param.Opt[string] `query:"identity_id,omitzero" json:"-"`
// Credential authentication material; supports `static_bearer` and `mcp_oauth`. The
// response only returns redacted, non-secret fields.
Auth map[string]any `json:"auth" api:"required"`
@@ -293,3 +297,7 @@ func (r MCPOAuthRefreshUpdateParam) MarshalJSON() ([]byte, error) {
func (r *MCPOAuthRefreshUpdateParam) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+
+func (r VaultCredentialNewParams) URLQuery() (url.Values, error) {
+ return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ArrayFormat: apiquery.ArrayQueryFormatRepeat, NestedFormat: apiquery.NestedQueryFormatBrackets})
+}
diff --git a/managed/deployment.go b/managed/deployment.go
index cc2c345..71e998b 100644
--- a/managed/deployment.go
+++ b/managed/deployment.go
@@ -243,8 +243,8 @@ type ManagedAgentsDeployment struct {
// Vault IDs supplying stored credentials for sessions created from this
// deployment.
VaultIDs []string `json:"vault_ids" api:"required"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
@@ -1848,8 +1848,8 @@ type DeploymentNewParams struct {
Name string `json:"name" api:"required"`
// Description of what the deployment does.
Description param.Opt[string] `json:"description,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up
// to 512 chars.
@@ -2045,8 +2045,8 @@ type DeploymentUpdateParams struct {
// version, or an `agent` object with both id and version specified. Omit to
// preserve. Cannot be cleared.
Agent DeploymentUpdateParamsAgentUnion `json:"agent,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Initial events. Full replacement. Omit to preserve. Cannot be cleared. At least
// 1, maximum 50.
diff --git a/managed/qoder_types_test.go b/managed/qoder_types_test.go
index 0137ab4..1bc9df1 100644
--- a/managed/qoder_types_test.go
+++ b/managed/qoder_types_test.go
@@ -67,6 +67,11 @@ func TestQoderFieldAlignment(t *testing.T) {
}
for name, fields := range expected {
for _, f := range fields {
+ // QCA uses decimal Credits, not the upstream monetary budget.
+ // https://docs.qoder.com/cloud-agents/api/sessions/schemas#session-budget
+ if (name == "ManagedAgentsBudgetLimit" || name == "ManagedAgentsBudgetLimitParam") && f == "MaxListCost" {
+ f = "MaxCreditCost"
+ }
if !actual[name][f] {
t.Errorf("upstream field missing: %s.%s", name, f)
}
diff --git a/managed/session.go b/managed/session.go
index 29d7604..9064603 100644
--- a/managed/session.go
+++ b/managed/session.go
@@ -540,19 +540,19 @@ func (r *ManagedAgentsBranchCheckoutParam) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
-// A hard spend ceiling. The session stops issuing new model requests once the
-// tracked list cost reaches `max_list_cost`.
+// A total model and sandbox credit limit. Once reached, new user messages are rejected;
+// an already running turn continues to completion.
type ManagedAgentsBudgetLimit struct {
- // A monetary amount in a specific currency.
- MaxListCost MonetaryAmount `json:"max_list_cost" api:"required"`
+ // Positive decimal Credits, with at most 12 integer and 2 fractional digits.
+ MaxCreditCost string `json:"max_credit_cost" api:"required"`
// Any of "limit".
Type ManagedAgentsBudgetLimitType `json:"type" api:"required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- MaxListCost respjson.Field
- Type respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ MaxCreditCost respjson.Field
+ Type respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -578,13 +578,13 @@ const (
ManagedAgentsBudgetLimitTypeLimit ManagedAgentsBudgetLimitType = "limit"
)
-// A hard spend ceiling. The session stops issuing new model requests once the
-// tracked list cost reaches `max_list_cost`.
+// A total model and sandbox credit limit. Once reached, new user messages are rejected;
+// an already running turn continues to completion.
//
-// The properties MaxListCost, Type are required.
+// The properties MaxCreditCost, Type are required.
type ManagedAgentsBudgetLimitParam struct {
- // A monetary amount in a specific currency.
- MaxListCost MonetaryAmountParam `json:"max_list_cost,omitzero" api:"required"`
+ // Positive decimal Credits, with at most 12 integer and 2 fractional digits.
+ MaxCreditCost string `json:"max_credit_cost" api:"required"`
// Any of "limit".
Type ManagedAgentsBudgetLimitType `json:"type,omitzero" api:"required"`
paramObj
@@ -1226,9 +1226,9 @@ type ManagedAgentsSession struct {
Agent ManagedAgentsSessionAgent `json:"agent" api:"required"`
// A timestamp in RFC 3339 format
ArchivedAt time.Time `json:"archived_at" api:"required" format:"date-time"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
- Budget ManagedAgentsBudgetLimit `json:"budget" api:"required"`
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
+ Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// A timestamp in RFC 3339 format
CreatedAt time.Time `json:"created_at" api:"required" format:"date-time"`
EnvironmentID string `json:"environment_id" api:"required"`
@@ -1248,8 +1248,9 @@ type ManagedAgentsSession struct {
Type ManagedAgentsSessionType `json:"type" api:"required"`
// A timestamp in RFC 3339 format
UpdatedAt time.Time `json:"updated_at" api:"required" format:"date-time"`
- // Cumulative token usage for a session across all turns.
- Usage ManagedAgentsSessionUsage `json:"usage" api:"required"`
+ // Cumulative Credits snapshot. Replace previous values; do not sum snapshots.
+ // Legacy token and list-cost fields remain available for older responses.
+ Usage ManagedAgentsSessionUsage `json:"usage"`
// Vault IDs attached to the session at creation. Empty when no vaults were
// supplied.
VaultIDs []string `json:"vault_ids" api:"required"`
@@ -1983,8 +1984,8 @@ type ManagedAgentsSessionUpdatedEvent struct {
// Resolved `agent` definition for a `session`. Snapshot of the `agent` at
// `session` creation time.
Agent ManagedAgentsSessionAgent `json:"agent" api:"nullable"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// The session's full metadata bag after the update. Present when the update set
// non-empty metadata; absent when metadata was unchanged or cleared to empty.
@@ -2017,8 +2018,12 @@ const (
ManagedAgentsSessionUpdatedEventTypeSessionUpdated ManagedAgentsSessionUpdatedEventType = "session.updated"
)
-// Cumulative token usage for a session across all turns.
+// Cumulative Credits snapshot. Replace previous values; do not sum snapshots.
+// Legacy token and list-cost fields remain available for older responses.
type ManagedAgentsSessionUsage struct {
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
// Cumulative time in seconds during which the session had at least one thread in
// running status. Overlapping activity from concurrent threads is counted once,
// unlike `stats.active_seconds`, which sums each thread's own active time. This is
@@ -2038,15 +2043,18 @@ type ManagedAgentsSessionUsage struct {
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- ActiveSeconds respjson.Field
- CacheCreation respjson.Field
- CacheReadInputTokens respjson.Field
- InputTokens respjson.Field
- ListCost respjson.Field
- OutputTokens respjson.Field
- ServerToolUse respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
+ ActiveSeconds respjson.Field
+ CacheCreation respjson.Field
+ CacheReadInputTokens respjson.Field
+ InputTokens respjson.Field
+ ListCost respjson.Field
+ OutputTokens respjson.Field
+ ServerToolUse respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -2056,8 +2064,12 @@ func (r *ManagedAgentsSessionUsage) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
-// Periodic snapshot of the session's cumulative usage and tracked list cost.
+// Cumulative Credits emitted before session.status_idle when usage is available.
+// Credits are top-level fields; Usage and Budget are legacy response fields.
type ManagedAgentsSessionUsageEvent struct {
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
// Unique identifier for this event.
ID string `json:"id" api:"required"`
// A timestamp in RFC 3339 format
@@ -2065,19 +2077,22 @@ type ManagedAgentsSessionUsageEvent struct {
// Any of "session.usage".
Type ManagedAgentsSessionUsageEventType `json:"type" api:"required"`
// Point-in-time snapshot of a session's cumulative usage.
- Usage ManagedAgentsSessionUsageSnapshot `json:"usage" api:"required"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ Usage ManagedAgentsSessionUsageSnapshot `json:"usage"`
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimit `json:"budget" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- ID respjson.Field
- ProcessedAt respjson.Field
- Type respjson.Field
- Usage respjson.Field
- Budget respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
+ ID respjson.Field
+ ProcessedAt respjson.Field
+ Type respjson.Field
+ Usage respjson.Field
+ Budget respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -2472,8 +2487,8 @@ type SessionNewParams struct {
EnvironmentID string `json:"environment_id" api:"required"`
// Human-readable session title.
Title param.Opt[string] `json:"title,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Initial events to send to the `session` at creation, processed in order.
// Supports `user.message` and `user.define_outcome` events. Maximum 50 events.
@@ -2804,8 +2819,8 @@ type SessionUpdateParams struct {
// updatable. Full replacement: the provided array becomes the new value. To
// preserve existing entries, GET the session, modify the array, and POST it back.
Agent ManagedAgentsSessionAgentUpdateParam `json:"agent,omitzero"`
- // A hard spend ceiling. The session stops issuing new model requests once the
- // tracked list cost reaches `max_list_cost`.
+ // A total model and sandbox credit limit. Once reached, new user messages are
+ // rejected; an already running turn continues to completion.
Budget ManagedAgentsBudgetLimitParam `json:"budget,omitzero"`
// Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests
// setting this field are rejected. Reserved for future use.
diff --git a/managed/session_credit_contract_test.go b/managed/session_credit_contract_test.go
new file mode 100644
index 0000000..17b9790
--- /dev/null
+++ b/managed/session_credit_contract_test.go
@@ -0,0 +1,124 @@
+package managed_test
+
+import (
+ "context"
+ "encoding/json"
+ "io"
+ "net/http"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/convention/param"
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
+ "github.com/QoderAI/qoder-cloud-agents-sdk-go/managed"
+)
+
+// Contract: https://docs.qoder.com/cloud-agents/api/sessions/schemas (2026-10-20).
+func TestSessionCreditBudgetWireContract(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ params managed.SessionUpdateParams
+ want string
+ }{
+ {"decimal", managed.SessionUpdateParams{Budget: managed.ManagedAgentsBudgetLimitParam{Type: "limit", MaxCreditCost: "100.00"}}, `{"budget":{"type":"limit","max_credit_cost":"100.00"}}`},
+ {"omit", managed.SessionUpdateParams{}, `{}`},
+ {"clear", managed.SessionUpdateParams{Budget: param.NullStruct[managed.ManagedAgentsBudgetLimitParam]()}, `{"budget":null}`},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ client := testClient(func(r *http.Request) (*http.Response, error) {
+ body, _ := io.ReadAll(r.Body)
+ var got, want any
+ if err := json.Unmarshal(body, &got); err != nil {
+ t.Fatal(err)
+ }
+ if err := json.Unmarshal([]byte(tc.want), &want); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %s want %s", body, tc.want)
+ }
+ return reply(r, 200, `{"id":"sess_one","budget":{"type":"limit","max_credit_cost":"100.00"}}`), nil
+ })
+ session, err := client.Sessions.Update(context.Background(), "sess_one", tc.params)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if session.Budget.MaxCreditCost != "100.00" {
+ t.Fatalf("budget=%+v", session.Budget)
+ }
+ })
+ }
+}
+
+func TestSessionCreditsSnapshotsAndEvents(t *testing.T) {
+ const usage = `{"model_credits":7.66,"sandbox_runtime_credits":0.34,"total_credits":8}`
+ var session managed.ManagedAgentsSession
+ if err := json.Unmarshal([]byte(`{"id":"sess_one","usage":`+usage+`}`), &session); err != nil {
+ t.Fatal(err)
+ }
+ if session.Usage.ModelCredits != 7.66 || session.Usage.SandboxRuntimeCredits != 0.34 || session.Usage.TotalCredits != 8 {
+ t.Fatalf("usage=%+v", session.Usage)
+ }
+ var fsession forward.Session
+ if err := json.Unmarshal([]byte(`{"id":"sess_one","usage":`+usage+`}`), &fsession); err != nil {
+ t.Fatal(err)
+ }
+ if fsession.Usage.ModelCredits != 7.66 || fsession.Usage.SandboxRuntimeCredits != 0.34 || fsession.Usage.TotalCredits != 8 {
+ t.Fatalf("forward usage=%+v", fsession.Usage)
+ }
+ var empty managed.ManagedAgentsSession
+ if err := json.Unmarshal([]byte(`{"id":"sess_empty"}`), &empty); err != nil {
+ t.Fatal(err)
+ }
+ if empty.JSON.Usage.Valid() {
+ t.Fatal("missing usage must stay absent")
+ }
+ const event = `{"id":"evt_one","type":"session.usage","model_credits":7.66,"sandbox_runtime_credits":0.34,"total_credits":8}`
+ var history managed.ManagedAgentsSessionEventUnion
+ if err := json.Unmarshal([]byte(event), &history); err != nil {
+ t.Fatal(err)
+ }
+ if history.TotalCredits != 8 || history.AsSessionUsage().TotalCredits != 8 {
+ t.Fatal("history lost top-level credits")
+ }
+ var thread managed.ManagedAgentsStreamSessionThreadEventsUnion
+ if err := json.Unmarshal([]byte(event), &thread); err != nil {
+ t.Fatal(err)
+ }
+ if thread.TotalCredits != 8 || thread.AsSessionUsage().ModelCredits != 7.66 {
+ t.Fatal("thread stream lost credits")
+ }
+ client := testClient(func(r *http.Request) (*http.Response, error) {
+ res := reply(r, 200, "id: evt_one\nevent: session.usage\ndata: "+event+"\n\nid: evt_two\nevent: agent.thread_context_compacted\ndata: {\"id\":\"evt_two\",\"type\":\"agent.thread_context_compacted\",\"usage\":{\"credits\":0}}\n\nid: evt_three\nevent: span.model_request_end\ndata: {\"id\":\"evt_three\",\"type\":\"span.model_request_end\",\"model_usage\":{\"credits\":1.25}}\n\n")
+ res.Header.Set("Content-Type", "text/event-stream")
+ return res, nil
+ })
+ stream := client.Sessions.Events.StreamEvents(context.Background(), "sess_one", managed.SessionEventStreamParams{})
+ defer stream.Close()
+ var seen []string
+ for stream.Next() {
+ e := stream.Current()
+ seen = append(seen, e.Type)
+ switch e.Type {
+ case "session.usage":
+ if e.TotalCredits != 8 || e.AsSessionUsage().TotalCredits != 8 {
+ t.Fatal("SSE lost credits")
+ }
+ case "agent.thread_context_compacted":
+ if !e.Usage.JSON.Credits.Valid() || e.AsAgentThreadContextCompacted().Usage.Credits != 0 {
+ t.Fatal("zero credits must be preserved")
+ }
+ case "span.model_request_end":
+ if e.ModelUsage.Credits != 1.25 {
+ t.Fatal("model credits lost")
+ }
+ }
+ }
+ if err := stream.Err(); err != nil {
+ t.Fatal(err)
+ }
+ if strings.Join(seen, ",") != "session.usage,agent.thread_context_compacted,span.model_request_end" {
+ t.Fatal(seen)
+ }
+}
diff --git a/managed/sessionevent.go b/managed/sessionevent.go
index 3c82925..d9a1e48 100644
--- a/managed/sessionevent.go
+++ b/managed/sessionevent.go
@@ -513,6 +513,8 @@ const (
// Indicates that context compaction (summarization) occurred during the session.
type ManagedAgentsAgentThreadContextCompactedEvent struct {
+ // Credits for this compaction call, already included in Session totals.
+ Usage ManagedAgentsSpanModelUsage `json:"usage"`
// Unique identifier for this event.
ID string `json:"id" api:"required"`
// A timestamp in RFC 3339 format
@@ -521,6 +523,7 @@ type ManagedAgentsAgentThreadContextCompactedEvent struct {
Type ManagedAgentsAgentThreadContextCompactedEventType `json:"type" api:"required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
+ Usage respjson.Field
ID respjson.Field
ProcessedAt respjson.Field
Type respjson.Field
@@ -3757,7 +3760,10 @@ const (
//
// Use the methods beginning with 'As' to cast the union to one of its variants.
type ManagedAgentsSessionEventUnion struct {
- ID string `json:"id"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
+ ID string `json:"id"`
// This field is a union of [[]ManagedAgentsUserMessageEventContentUnion],
// [[]ManagedAgentsUserCustomToolResultEventContentUnion],
// [[]ManagedAgentsAgentMessageEventContentUnion],
@@ -3842,6 +3848,9 @@ type ManagedAgentsSessionEventUnion struct {
// This field is from variant [ManagedAgentsSessionUpdatedEvent].
Title string `json:"title"`
JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
ID respjson.Field
Content respjson.Field
Type respjson.Field
@@ -4321,6 +4330,7 @@ func (r *ManagedAgentsSessionEventUnionStopReason) UnmarshalJSON(data []byte) er
// For type safety it is recommended to directly use a variant of the
// [ManagedAgentsSessionEventUnion].
type ManagedAgentsSessionEventUnionUsage struct {
+ Credits float64 `json:"credits"`
// This field is from variant [ManagedAgentsSpanModelUsage].
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
@@ -4337,6 +4347,7 @@ type ManagedAgentsSessionEventUnionUsage struct {
// This field is from variant [ManagedAgentsSessionUsageSnapshot].
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use"`
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
@@ -4949,8 +4960,8 @@ type ManagedAgentsSpanModelRequestEndEvent struct {
IsError bool `json:"is_error" api:"required"`
// The id of the corresponding `span.model_request_start` event.
ModelRequestStartID string `json:"model_request_start_id" api:"required"`
- // Token usage for a single model request.
- ModelUsage ManagedAgentsSpanModelUsage `json:"model_usage" api:"required"`
+ // Credits for a single model request; legacy token fields may appear in older responses.
+ ModelUsage ManagedAgentsSpanModelUsage `json:"model_usage"`
// A timestamp in RFC 3339 format
ProcessedAt time.Time `json:"processed_at" api:"required" format:"date-time"`
// Any of "span.model_request_end".
@@ -5010,16 +5021,17 @@ const (
ManagedAgentsSpanModelRequestStartEventTypeSpanModelRequestStart ManagedAgentsSpanModelRequestStartEventType = "span.model_request_start"
)
-// Token usage for a single model request.
+// Credits for a single model request; legacy token fields may appear in older responses.
type ManagedAgentsSpanModelUsage struct {
+ Credits float64 `json:"credits"`
// Tokens used to create prompt cache in this request.
- CacheCreationInputTokens int64 `json:"cache_creation_input_tokens" api:"required"`
+ CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
// Tokens read from prompt cache in this request.
- CacheReadInputTokens int64 `json:"cache_read_input_tokens" api:"required"`
+ CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
// Input tokens consumed by this request.
- InputTokens int64 `json:"input_tokens" api:"required"`
+ InputTokens int64 `json:"input_tokens"`
// Output tokens generated by this request.
- OutputTokens int64 `json:"output_tokens" api:"required"`
+ OutputTokens int64 `json:"output_tokens"`
// Inference speed mode. `fast` provides significantly faster output token
// generation at premium pricing. Not all models support `fast`; invalid
// combinations are rejected at create time.
@@ -5028,6 +5040,7 @@ type ManagedAgentsSpanModelUsage struct {
Speed ManagedAgentsSpanModelUsageSpeed `json:"speed" api:"nullable"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
@@ -5083,7 +5096,7 @@ type ManagedAgentsSpanOutcomeEvaluationEndEvent struct {
Result string `json:"result" api:"required"`
// Any of "span.outcome_evaluation_end".
Type ManagedAgentsSpanOutcomeEvaluationEndEventType `json:"type" api:"required"`
- // Token usage for a single model request.
+ // Credits for a single model request; legacy token fields may appear in older responses.
Usage ManagedAgentsSpanModelUsage `json:"usage" api:"required"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
@@ -5230,7 +5243,10 @@ const (
//
// Use the methods beginning with 'As' to cast the union to one of its variants.
type ManagedAgentsStreamSessionEventsUnion struct {
- ID string `json:"id"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
+ ID string `json:"id"`
// This field is a union of [[]ManagedAgentsUserMessageEventContentUnion],
// [[]ManagedAgentsUserCustomToolResultEventContentUnion],
// [[]ManagedAgentsAgentMessageEventContentUnion],
@@ -5321,6 +5337,9 @@ type ManagedAgentsStreamSessionEventsUnion struct {
// This field is from variant [ManagedAgentsDeltaEvent].
EventID string `json:"event_id"`
JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
ID respjson.Field
Content respjson.Field
Type respjson.Field
@@ -5833,6 +5852,7 @@ func (r *ManagedAgentsStreamSessionEventsUnionStopReason) UnmarshalJSON(data []b
// For type safety it is recommended to directly use a variant of the
// [ManagedAgentsStreamSessionEventsUnion].
type ManagedAgentsStreamSessionEventsUnionUsage struct {
+ Credits float64 `json:"credits"`
// This field is from variant [ManagedAgentsSpanModelUsage].
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
@@ -5849,6 +5869,7 @@ type ManagedAgentsStreamSessionEventsUnionUsage struct {
// This field is from variant [ManagedAgentsSessionUsageSnapshot].
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use"`
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field
diff --git a/managed/sessionthread.go b/managed/sessionthread.go
index d22c0b3..9e5ac57 100644
--- a/managed/sessionthread.go
+++ b/managed/sessionthread.go
@@ -407,7 +407,10 @@ func (r *ManagedAgentsSessionThreadUsage) UnmarshalJSON(data []byte) error {
//
// Use the methods beginning with 'As' to cast the union to one of its variants.
type ManagedAgentsStreamSessionThreadEventsUnion struct {
- ID string `json:"id"`
+ ModelCredits float64 `json:"model_credits"`
+ SandboxRuntimeCredits float64 `json:"sandbox_runtime_credits"`
+ TotalCredits float64 `json:"total_credits"`
+ ID string `json:"id"`
// This field is a union of [[]ManagedAgentsUserMessageEventContentUnion],
// [[]ManagedAgentsUserCustomToolResultEventContentUnion],
// [[]ManagedAgentsAgentMessageEventContentUnion],
@@ -498,6 +501,9 @@ type ManagedAgentsStreamSessionThreadEventsUnion struct {
// This field is from variant [ManagedAgentsDeltaEvent].
EventID string `json:"event_id"`
JSON struct {
+ ModelCredits respjson.Field
+ SandboxRuntimeCredits respjson.Field
+ TotalCredits respjson.Field
ID respjson.Field
Content respjson.Field
Type respjson.Field
@@ -1021,6 +1027,7 @@ func (r *ManagedAgentsStreamSessionThreadEventsUnionStopReason) UnmarshalJSON(da
// For type safety it is recommended to directly use a variant of the
// [ManagedAgentsStreamSessionThreadEventsUnion].
type ManagedAgentsStreamSessionThreadEventsUnionUsage struct {
+ Credits float64 `json:"credits"`
// This field is from variant [ManagedAgentsSpanModelUsage].
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
@@ -1037,6 +1044,7 @@ type ManagedAgentsStreamSessionThreadEventsUnionUsage struct {
// This field is from variant [ManagedAgentsSessionUsageSnapshot].
ServerToolUse ManagedAgentsServerToolUsage `json:"server_tool_use"`
JSON struct {
+ Credits respjson.Field
CacheCreationInputTokens respjson.Field
CacheReadInputTokens respjson.Field
InputTokens respjson.Field