Add experimental thread job scheduler for /loop

This commit is contained in:
Eric Traut
2026-03-30 15:30:34 -06:00
parent 716f7b0428
commit a02c2cd50c
52 changed files with 2291 additions and 113 deletions
@@ -2985,6 +2985,81 @@
}
]
},
"ThreadJob": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"id": {
"type": "string"
},
"lastRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"nextRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
}
},
"required": [
"createdAt",
"cronExpression",
"id",
"prompt",
"runOnce"
],
"type": "object"
},
"ThreadJobFiredNotification": {
"properties": {
"job": {
"$ref": "#/definitions/ThreadJob"
},
"threadId": {
"type": "string"
}
},
"required": [
"job",
"threadId"
],
"type": "object"
},
"ThreadJobUpdatedNotification": {
"properties": {
"jobs": {
"items": {
"$ref": "#/definitions/ThreadJob"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"jobs",
"threadId"
],
"type": "object"
},
"ThreadNameUpdatedNotification": {
"properties": {
"threadId": {
@@ -3940,6 +4015,46 @@
"title": "Thread/name/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/job/updated"
],
"title": "Thread/job/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadJobUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/job/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/job/fired"
],
"title": "Thread/job/firedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadJobFiredNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/job/firedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -3557,6 +3557,46 @@
"title": "Thread/name/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/job/updated"
],
"title": "Thread/job/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadJobUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/job/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/job/fired"
],
"title": "Thread/job/firedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadJobFiredNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/job/firedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -12960,6 +13000,85 @@
}
]
},
"ThreadJob": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"id": {
"type": "string"
},
"lastRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"nextRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
}
},
"required": [
"createdAt",
"cronExpression",
"id",
"prompt",
"runOnce"
],
"type": "object"
},
"ThreadJobFiredNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"job": {
"$ref": "#/definitions/v2/ThreadJob"
},
"threadId": {
"type": "string"
}
},
"required": [
"job",
"threadId"
],
"title": "ThreadJobFiredNotification",
"type": "object"
},
"ThreadJobUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"jobs": {
"items": {
"$ref": "#/definitions/v2/ThreadJob"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"jobs",
"threadId"
],
"title": "ThreadJobUpdatedNotification",
"type": "object"
},
"ThreadListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -8344,6 +8344,46 @@
"title": "Thread/name/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/job/updated"
],
"title": "Thread/job/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadJobUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/job/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/job/fired"
],
"title": "Thread/job/firedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadJobFiredNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/job/firedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -10806,6 +10846,85 @@
}
]
},
"ThreadJob": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"id": {
"type": "string"
},
"lastRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"nextRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
}
},
"required": [
"createdAt",
"cronExpression",
"id",
"prompt",
"runOnce"
],
"type": "object"
},
"ThreadJobFiredNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"job": {
"$ref": "#/definitions/ThreadJob"
},
"threadId": {
"type": "string"
}
},
"required": [
"job",
"threadId"
],
"title": "ThreadJobFiredNotification",
"type": "object"
},
"ThreadJobUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"jobs": {
"items": {
"$ref": "#/definitions/ThreadJob"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"jobs",
"threadId"
],
"title": "ThreadJobUpdatedNotification",
"type": "object"
},
"ThreadListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -0,0 +1,61 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ThreadJob": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"id": {
"type": "string"
},
"lastRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"nextRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
}
},
"required": [
"createdAt",
"cronExpression",
"id",
"prompt",
"runOnce"
],
"type": "object"
}
},
"properties": {
"job": {
"$ref": "#/definitions/ThreadJob"
},
"threadId": {
"type": "string"
}
},
"required": [
"job",
"threadId"
],
"title": "ThreadJobFiredNotification",
"type": "object"
}
@@ -0,0 +1,64 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ThreadJob": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"cronExpression": {
"type": "string"
},
"id": {
"type": "string"
},
"lastRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"nextRunAt": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"prompt": {
"type": "string"
},
"runOnce": {
"type": "boolean"
}
},
"required": [
"createdAt",
"cronExpression",
"id",
"prompt",
"runOnce"
],
"type": "object"
}
},
"properties": {
"jobs": {
"items": {
"$ref": "#/definitions/ThreadJob"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"jobs",
"threadId"
],
"title": "ThreadJobUpdatedNotification",
"type": "object"
}
@@ -36,6 +36,8 @@ import type { SkillsChangedNotification } from "./v2/SkillsChangedNotification";
import type { TerminalInteractionNotification } from "./v2/TerminalInteractionNotification";
import type { ThreadArchivedNotification } from "./v2/ThreadArchivedNotification";
import type { ThreadClosedNotification } from "./v2/ThreadClosedNotification";
import type { ThreadJobFiredNotification } from "./v2/ThreadJobFiredNotification";
import type { ThreadJobUpdatedNotification } from "./v2/ThreadJobUpdatedNotification";
import type { ThreadNameUpdatedNotification } from "./v2/ThreadNameUpdatedNotification";
import type { ThreadRealtimeClosedNotification } from "./v2/ThreadRealtimeClosedNotification";
import type { ThreadRealtimeErrorNotification } from "./v2/ThreadRealtimeErrorNotification";
@@ -57,4 +59,4 @@ import type { WindowsWorldWritableWarningNotification } from "./v2/WindowsWorldW
/**
* Notification sent from the server to the client.
*/
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "fs/changed", "params": FsChangedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcriptUpdated", "params": ThreadRealtimeTranscriptUpdatedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/job/updated", "params": ThreadJobUpdatedNotification } | { "method": "thread/job/fired", "params": ThreadJobFiredNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "fs/changed", "params": FsChangedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcriptUpdated", "params": ThreadRealtimeTranscriptUpdatedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ThreadJob = { id: string, cronExpression: string, prompt: string, runOnce: boolean, createdAt: number, nextRunAt: number | null, lastRunAt: number | null, };
@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ThreadJob } from "./ThreadJob";
export type ThreadJobFiredNotification = { threadId: string, job: ThreadJob, };
@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ThreadJob } from "./ThreadJob";
export type ThreadJobUpdatedNotification = { threadId: string, jobs: Array<ThreadJob>, };
@@ -274,6 +274,9 @@ export type { ThreadCompactStartResponse } from "./ThreadCompactStartResponse";
export type { ThreadForkParams } from "./ThreadForkParams";
export type { ThreadForkResponse } from "./ThreadForkResponse";
export type { ThreadItem } from "./ThreadItem";
export type { ThreadJob } from "./ThreadJob";
export type { ThreadJobFiredNotification } from "./ThreadJobFiredNotification";
export type { ThreadJobUpdatedNotification } from "./ThreadJobUpdatedNotification";
export type { ThreadListParams } from "./ThreadListParams";
export type { ThreadListResponse } from "./ThreadListResponse";
export type { ThreadLoadedListParams } from "./ThreadLoadedListParams";
@@ -317,6 +317,21 @@ client_request_definitions! {
params: v2::ThreadReadParams,
response: v2::ThreadReadResponse,
},
#[experimental("thread/job/create")]
ThreadJobCreate => "thread/job/create" {
params: v2::ThreadJobCreateParams,
response: v2::ThreadJobCreateResponse,
},
#[experimental("thread/job/delete")]
ThreadJobDelete => "thread/job/delete" {
params: v2::ThreadJobDeleteParams,
response: v2::ThreadJobDeleteResponse,
},
#[experimental("thread/job/list")]
ThreadJobList => "thread/job/list" {
params: v2::ThreadJobListParams,
response: v2::ThreadJobListResponse,
},
SkillsList => "skills/list" {
params: v2::SkillsListParams,
response: v2::SkillsListResponse,
@@ -918,6 +933,10 @@ server_notification_definitions! {
ThreadClosed => "thread/closed" (v2::ThreadClosedNotification),
SkillsChanged => "skills/changed" (v2::SkillsChangedNotification),
ThreadNameUpdated => "thread/name/updated" (v2::ThreadNameUpdatedNotification),
#[experimental("thread/job/updated")]
ThreadJobUpdated => "thread/job/updated" (v2::ThreadJobUpdatedNotification),
#[experimental("thread/job/fired")]
ThreadJobFired => "thread/job/fired" (v2::ThreadJobFiredNotification),
ThreadTokenUsageUpdated => "thread/tokenUsage/updated" (v2::ThreadTokenUsageUpdatedNotification),
TurnStarted => "turn/started" (v2::TurnStartedNotification),
HookStarted => "hook/started" (v2::HookStartedNotification),
@@ -3149,6 +3149,85 @@ pub struct ThreadReadResponse {
pub thread: Thread,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJob {
pub id: String,
pub cron_expression: String,
pub prompt: String,
pub run_once: bool,
#[ts(type = "number")]
pub created_at: i64,
#[ts(type = "number | null")]
pub next_run_at: Option<i64>,
#[ts(type = "number | null")]
pub last_run_at: Option<i64>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobCreateParams {
pub thread_id: String,
pub cron_expression: String,
pub prompt: String,
#[ts(optional = nullable)]
pub run_once: Option<bool>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobCreateResponse {
pub job: ThreadJob,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobDeleteParams {
pub thread_id: String,
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobDeleteResponse {
pub deleted: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobListParams {
pub thread_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobListResponse {
pub data: Vec<ThreadJob>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobUpdatedNotification {
pub thread_id: String,
pub jobs: Vec<ThreadJob>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadJobFiredNotification {
pub thread_id: String,
pub job: ThreadJob,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]