@tai42/api-client 11.1.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/client.d.ts +313 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +89 -0
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +489 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +280 -3
- package/dist/schemas.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +190 -0
- package/src/index.ts +35 -0
- package/src/schemas.ts +345 -3
package/src/client.ts
CHANGED
|
@@ -498,6 +498,66 @@ export interface SetMcpSecretEnvBody {
|
|
|
498
498
|
readonly manifest_pointer: string;
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
+
/**
|
|
502
|
+
* The client-facing write body for a state declaration (`PUT /api/states/{name}`), the
|
|
503
|
+
* wire shape of `tai42_contract.states.StateDeclaration` minus the platform-computed
|
|
504
|
+
* `effective_schema`/`regimes` (those are read-only, refused on a write). `retention_days`
|
|
505
|
+
* is `null` to keep records forever.
|
|
506
|
+
*/
|
|
507
|
+
export interface StateDeclarationBody {
|
|
508
|
+
readonly name: string;
|
|
509
|
+
readonly description?: string;
|
|
510
|
+
readonly schema: Record<string, unknown>;
|
|
511
|
+
readonly subject_kinds: readonly string[];
|
|
512
|
+
readonly default_subject_kind: string;
|
|
513
|
+
readonly retention_days?: number | null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** A state-module document write body (`PUT /api/state-modules/{name}`, the platform half). */
|
|
517
|
+
export interface StateModuleBody {
|
|
518
|
+
readonly name: string;
|
|
519
|
+
readonly description?: string;
|
|
520
|
+
readonly parameters?: Record<string, unknown>;
|
|
521
|
+
readonly schema: Record<string, unknown>;
|
|
522
|
+
readonly regimes?: Record<string, unknown>[];
|
|
523
|
+
readonly declarations?: Record<string, unknown> | null;
|
|
524
|
+
readonly trace?: Record<string, unknown>;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** A module mount / re-mount body (`PUT|PATCH /api/states/{name}/mounts/{module}`). */
|
|
528
|
+
export interface StateMountBody {
|
|
529
|
+
readonly path?: string[];
|
|
530
|
+
readonly parameters?: Record<string, unknown>;
|
|
531
|
+
readonly declarations?: Record<string, unknown>;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** The four-part subject a record route addresses in its path. */
|
|
535
|
+
export interface StateSubjectRef {
|
|
536
|
+
readonly target_kind: string;
|
|
537
|
+
readonly target_name: string;
|
|
538
|
+
readonly kind: string;
|
|
539
|
+
readonly key: string;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/** A keyset page request: an optional size and the opaque cursor a prior page returned. */
|
|
543
|
+
export interface StatePageQuery {
|
|
544
|
+
readonly limit?: number;
|
|
545
|
+
readonly cursor?: string;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* The subject-addressed record path: `/api/states/{name}/records/{target_kind}/
|
|
550
|
+
* {target_name}/{kind}/{key}` — four path segments, each percent-encoded, so a key
|
|
551
|
+
* carrying a slash or a colon never escapes its segment.
|
|
552
|
+
*/
|
|
553
|
+
function stateRecordPath(name: string, subject: StateSubjectRef): string {
|
|
554
|
+
return (
|
|
555
|
+
`/api/states/${encodeSegment(name)}/records/` +
|
|
556
|
+
`${encodeSegment(subject.target_kind)}/${encodeSegment(subject.target_name)}/` +
|
|
557
|
+
`${encodeSegment(subject.kind)}/${encodeSegment(subject.key)}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
501
561
|
export function createApiClient(config: ApiConfig) {
|
|
502
562
|
const req = <S extends Parameters<typeof apiRequest>[2]>(
|
|
503
563
|
path: string,
|
|
@@ -606,6 +666,126 @@ export function createApiClient(config: ApiConfig) {
|
|
|
606
666
|
{ method: 'PUT', body: { tags } },
|
|
607
667
|
),
|
|
608
668
|
|
|
669
|
+
// -- states --------------------------------------------------------------
|
|
670
|
+
// A state is a declared JSON document, one per subject. The list is the
|
|
671
|
+
// management population; a single state read composes the declaration with its
|
|
672
|
+
// effective schema, regimes and mounts. Every record route addresses its subject
|
|
673
|
+
// as four percent-encoded path segments (see `stateRecordPath`).
|
|
674
|
+
listStates: (signal?: AbortSignal) => req('/api/states', s.stateList, { signal }),
|
|
675
|
+
getState: (name: string, signal?: AbortSignal) =>
|
|
676
|
+
req(`/api/states/${encodeSegment(name)}`, s.stateDetail, { signal }),
|
|
677
|
+
// A plain declaration upsert — no `replace` flag. With records present the server
|
|
678
|
+
// accepts additive schema changes and refuses with a 409 any change that removes or
|
|
679
|
+
// alters an existing field, or a subject-kind removal that would strand records.
|
|
680
|
+
putState: (name: string, body: StateDeclarationBody) =>
|
|
681
|
+
req(`/api/states/${encodeSegment(name)}`, s.stateDetail, {
|
|
682
|
+
method: 'PUT',
|
|
683
|
+
body,
|
|
684
|
+
}),
|
|
685
|
+
deleteState: (name: string) =>
|
|
686
|
+
req(`/api/states/${encodeSegment(name)}`, s.stateDeleted, { method: 'DELETE' }),
|
|
687
|
+
getStateStats: (name: string, signal?: AbortSignal) =>
|
|
688
|
+
req(`/api/states/${encodeSegment(name)}/stats`, s.stateStats, { signal }),
|
|
689
|
+
// -- state mounts --------------------------------------------------------
|
|
690
|
+
listStateMounts: (name: string, signal?: AbortSignal) =>
|
|
691
|
+
req(`/api/states/${encodeSegment(name)}/mounts`, s.stateMountList, { signal }),
|
|
692
|
+
mountStateModule: (name: string, module: string, body: StateMountBody) =>
|
|
693
|
+
req(`/api/states/${encodeSegment(name)}/mounts/${encodeSegment(module)}`, s.stateMounted, {
|
|
694
|
+
method: 'PUT',
|
|
695
|
+
body,
|
|
696
|
+
}),
|
|
697
|
+
patchStateMount: (name: string, module: string, body: StateMountBody) =>
|
|
698
|
+
req(
|
|
699
|
+
`/api/states/${encodeSegment(name)}/mounts/${encodeSegment(module)}`,
|
|
700
|
+
s.stateMountUpdated,
|
|
701
|
+
{ method: 'PATCH', body },
|
|
702
|
+
),
|
|
703
|
+
unmountStateModule: (name: string, module: string) =>
|
|
704
|
+
req(`/api/states/${encodeSegment(name)}/mounts/${encodeSegment(module)}`, s.stateUnmounted, {
|
|
705
|
+
method: 'DELETE',
|
|
706
|
+
}),
|
|
707
|
+
// -- state subjects + records -------------------------------------------
|
|
708
|
+
listStateSubjects: (
|
|
709
|
+
name: string,
|
|
710
|
+
params: { kind?: string } & StatePageQuery,
|
|
711
|
+
signal?: AbortSignal,
|
|
712
|
+
) =>
|
|
713
|
+
req(`/api/states/${encodeSegment(name)}/subjects`, s.subjectPage, {
|
|
714
|
+
signal,
|
|
715
|
+
query: { kind: params.kind, limit: params.limit, cursor: params.cursor },
|
|
716
|
+
}),
|
|
717
|
+
// The content search body is a single JSONB containment document (`filters`); the
|
|
718
|
+
// server refuses an empty one, so the UI validates a non-empty object first.
|
|
719
|
+
searchStateRecords: (
|
|
720
|
+
name: string,
|
|
721
|
+
body: { filters: Record<string, unknown> } & StatePageQuery,
|
|
722
|
+
signal?: AbortSignal,
|
|
723
|
+
) =>
|
|
724
|
+
req(`/api/states/${encodeSegment(name)}/records/search`, s.recordSearchPage, {
|
|
725
|
+
method: 'POST',
|
|
726
|
+
body,
|
|
727
|
+
signal,
|
|
728
|
+
}),
|
|
729
|
+
// A subject with no document yet reads as `null` (a 200 `{data: null}`, the record
|
|
730
|
+
// page's Create path) — never an error, so the schema is nullable.
|
|
731
|
+
getStateRecord: (name: string, subject: StateSubjectRef, signal?: AbortSignal) =>
|
|
732
|
+
req(stateRecordPath(name, subject), s.recordView.nullable(), { signal }),
|
|
733
|
+
putStateRecord: (name: string, subject: StateSubjectRef, data: Record<string, unknown>) =>
|
|
734
|
+
req(stateRecordPath(name, subject), s.recordView, { method: 'PUT', body: data }),
|
|
735
|
+
patchStateRecord: (name: string, subject: StateSubjectRef, data: Record<string, unknown>) =>
|
|
736
|
+
req(stateRecordPath(name, subject), s.recordView, { method: 'PATCH', body: data }),
|
|
737
|
+
applyStateRecord: (
|
|
738
|
+
name: string,
|
|
739
|
+
subject: StateSubjectRef,
|
|
740
|
+
ops: readonly Record<string, unknown>[],
|
|
741
|
+
) =>
|
|
742
|
+
req(`${stateRecordPath(name, subject)}/deltas`, s.applyResult, {
|
|
743
|
+
method: 'POST',
|
|
744
|
+
body: { ops },
|
|
745
|
+
}),
|
|
746
|
+
deleteStateRecord: (name: string, subject: StateSubjectRef) =>
|
|
747
|
+
req(stateRecordPath(name, subject), s.recordErased, { method: 'DELETE' }),
|
|
748
|
+
// `mode` is required server-side (`switch` drops, `merge` combines); the fold returns
|
|
749
|
+
// a report, not the surviving record.
|
|
750
|
+
foldStateRecord: (
|
|
751
|
+
name: string,
|
|
752
|
+
subject: StateSubjectRef,
|
|
753
|
+
into: StateSubjectRef,
|
|
754
|
+
mode: 'switch' | 'merge',
|
|
755
|
+
) =>
|
|
756
|
+
req(`${stateRecordPath(name, subject)}/fold`, s.stateFoldReport, {
|
|
757
|
+
method: 'POST',
|
|
758
|
+
body: { into, mode },
|
|
759
|
+
}),
|
|
760
|
+
listStateWrites: (
|
|
761
|
+
name: string,
|
|
762
|
+
subject: StateSubjectRef,
|
|
763
|
+
params?: StatePageQuery,
|
|
764
|
+
signal?: AbortSignal,
|
|
765
|
+
) =>
|
|
766
|
+
req(`${stateRecordPath(name, subject)}/writes`, s.writesPage, {
|
|
767
|
+
signal,
|
|
768
|
+
query: { limit: params?.limit, cursor: params?.cursor },
|
|
769
|
+
}),
|
|
770
|
+
stateConsumers: (name: string, signal?: AbortSignal) =>
|
|
771
|
+
req(`/api/states/${encodeSegment(name)}/consumers`, s.stateConsumers, { signal }),
|
|
772
|
+
// -- state-module documents (top-level sibling collection) ---------------
|
|
773
|
+
listStateModules: (signal?: AbortSignal) =>
|
|
774
|
+
req('/api/state-modules', s.stateModuleList, { signal }),
|
|
775
|
+
getStateModule: (name: string, signal?: AbortSignal) =>
|
|
776
|
+
req(`/api/state-modules/${encodeSegment(name)}`, s.stateModuleDocument, { signal }),
|
|
777
|
+
putStateModule: (name: string, body: StateModuleBody, replace?: boolean) =>
|
|
778
|
+
req(`/api/state-modules/${encodeSegment(name)}`, s.stateModuleDocument, {
|
|
779
|
+
method: 'PUT',
|
|
780
|
+
body,
|
|
781
|
+
query: replace === true ? { replace: 'true' } : undefined,
|
|
782
|
+
}),
|
|
783
|
+
deleteStateModule: (name: string) =>
|
|
784
|
+
req(`/api/state-modules/${encodeSegment(name)}`, s.stateModuleDeleted, { method: 'DELETE' }),
|
|
785
|
+
// -- state retention -----------------------------------------------------
|
|
786
|
+
pruneStateRetention: () =>
|
|
787
|
+
req('/api/state-retention/prune', s.stateRetentionPruned, { method: 'POST', body: {} }),
|
|
788
|
+
|
|
609
789
|
// -- tool_meta (the organizational overlay) ------------------------------
|
|
610
790
|
// The folder tree + per-tool overlay rows in one read; the features merge the
|
|
611
791
|
// rows against the live tool list and native tags client-side.
|
|
@@ -919,6 +1099,16 @@ export function createApiClient(config: ApiConfig) {
|
|
|
919
1099
|
method: 'POST',
|
|
920
1100
|
body: { answer },
|
|
921
1101
|
}),
|
|
1102
|
+
// Withdraw a pending ask WITHOUT answering it (a bodyless POST): the question is
|
|
1103
|
+
// resolved as cancelled and the flow that asked never resumes. Terminal like an
|
|
1104
|
+
// answer, so the loud failure mappings mirror it — a 409 (already answered, so no
|
|
1105
|
+
// longer cancellable) surfaces as `ApiConflictError`, a 404 (unknown/already gone)
|
|
1106
|
+
// and a 403 (not this caller's audience) as `ApiError` — all via the shared
|
|
1107
|
+
// `apiRequest` status handling.
|
|
1108
|
+
cancelInteraction: (interactionId: string) =>
|
|
1109
|
+
req(`/api/interactions/${encodeSegment(interactionId)}/cancel`, s.interactionCancelled, {
|
|
1110
|
+
method: 'POST',
|
|
1111
|
+
}),
|
|
922
1112
|
|
|
923
1113
|
// -- channels --------------------------------------------------------------
|
|
924
1114
|
// The installed channel-plugin names (delivery media for ask_user questions).
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,11 @@ export type {
|
|
|
25
25
|
CreatePresetBody,
|
|
26
26
|
SavePresetVersionBody,
|
|
27
27
|
ValidatePresetBody,
|
|
28
|
+
StateDeclarationBody,
|
|
29
|
+
StateModuleBody,
|
|
30
|
+
StateMountBody,
|
|
31
|
+
StateSubjectRef,
|
|
32
|
+
StatePageQuery,
|
|
28
33
|
ToolMetaPatch,
|
|
29
34
|
StorageUploadBody,
|
|
30
35
|
ConversationThreadFilters,
|
|
@@ -101,6 +106,30 @@ export type {
|
|
|
101
106
|
PresetValidation,
|
|
102
107
|
PresetVersionTags,
|
|
103
108
|
PresetExtensionElement,
|
|
109
|
+
StateSubject,
|
|
110
|
+
StateDeclaration,
|
|
111
|
+
StateListItem,
|
|
112
|
+
StateMount,
|
|
113
|
+
StateDetail,
|
|
114
|
+
StateStats,
|
|
115
|
+
StateModuleDocument,
|
|
116
|
+
StateModuleListItem,
|
|
117
|
+
RecordView,
|
|
118
|
+
ApplyResult,
|
|
119
|
+
SubjectRow,
|
|
120
|
+
SubjectPage,
|
|
121
|
+
RecordSearchPage,
|
|
122
|
+
CompletedOrigin,
|
|
123
|
+
WriteEntry,
|
|
124
|
+
WritesPage,
|
|
125
|
+
ConsumerLink,
|
|
126
|
+
ConsumerRow,
|
|
127
|
+
StateConsumers,
|
|
128
|
+
StateMounted,
|
|
129
|
+
StateMountUpdated,
|
|
130
|
+
StateUnmounted,
|
|
131
|
+
StateFoldReport,
|
|
132
|
+
StateRetentionPruned,
|
|
104
133
|
FolderRecord,
|
|
105
134
|
ToolMetaRecord,
|
|
106
135
|
ToolMetaOverlay,
|
|
@@ -122,8 +151,13 @@ export type {
|
|
|
122
151
|
StudioPluginManifest,
|
|
123
152
|
Interaction,
|
|
124
153
|
InteractionAnswered,
|
|
154
|
+
InteractionCancelled,
|
|
125
155
|
InteractionsPage,
|
|
126
156
|
InteractionMediaItem,
|
|
157
|
+
FormOption,
|
|
158
|
+
FormData,
|
|
159
|
+
FormPage,
|
|
160
|
+
FormPages,
|
|
127
161
|
MediaKind,
|
|
128
162
|
AnswerFormat,
|
|
129
163
|
Channels,
|
|
@@ -163,6 +197,7 @@ export type {
|
|
|
163
197
|
Notifications,
|
|
164
198
|
ChannelTemplate,
|
|
165
199
|
HookParams,
|
|
200
|
+
HookSubject,
|
|
166
201
|
HookList,
|
|
167
202
|
TriggerAuth,
|
|
168
203
|
SettingsSchema,
|
package/src/schemas.ts
CHANGED
|
@@ -943,14 +943,54 @@ export const interactionMediaItem = z.object({
|
|
|
943
943
|
});
|
|
944
944
|
export type InteractionMediaItem = z.infer<typeof interactionMediaItem>;
|
|
945
945
|
|
|
946
|
+
/**
|
|
947
|
+
* One per-send choice for a `form` property whose schema is a string (or array of
|
|
948
|
+
* strings). `value` is what the answer carries; `label` (absent OR null both parse
|
|
949
|
+
* to no label) is the human text shown in its place. A send may replace a property's
|
|
950
|
+
* `enum` this way for one ask without republishing the form. Applied per property by
|
|
951
|
+
* the form preview (safeParsed), so a malformed entry is a loud notice, never silent.
|
|
952
|
+
*/
|
|
953
|
+
export const formOption = z.object({
|
|
954
|
+
value: z.string(),
|
|
955
|
+
label: z.string().nullish(),
|
|
956
|
+
});
|
|
957
|
+
export type FormOption = z.infer<typeof formOption>;
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Per-send `form` data: `values` prefills top-level properties (keyed by property
|
|
961
|
+
* name), `options` supplies per-send choice lists (keyed by property name). Both
|
|
962
|
+
* default to empty. The form preview safeParses this off `format_payload.data`, so a
|
|
963
|
+
* malformed block is a loud notice rather than a whole-frame parse failure.
|
|
964
|
+
*/
|
|
965
|
+
export const formData = z.object({
|
|
966
|
+
values: z.record(z.string(), z.unknown()).default({}),
|
|
967
|
+
options: z.record(z.string(), z.array(formOption)).default({}),
|
|
968
|
+
});
|
|
969
|
+
export type FormData = z.infer<typeof formData>;
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* One page of a stepped `form`: a `title` and the ordered top-level property names it
|
|
973
|
+
* groups. `format_payload.pages` is the ordered list of these; absent = one page. The
|
|
974
|
+
* form preview safeParses the list, so a malformed page is a loud notice, never silent.
|
|
975
|
+
*/
|
|
976
|
+
export const formPage = z.object({
|
|
977
|
+
title: z.string(),
|
|
978
|
+
fields: z.array(z.string()),
|
|
979
|
+
});
|
|
980
|
+
export type FormPage = z.infer<typeof formPage>;
|
|
981
|
+
|
|
982
|
+
/** The `form` pages list (`format_payload.pages`), safeParsed as a whole by the preview. */
|
|
983
|
+
export const formPages = z.array(formPage);
|
|
984
|
+
export type FormPages = z.infer<typeof formPages>;
|
|
985
|
+
|
|
946
986
|
export const interaction = z.object({
|
|
947
987
|
interaction_id: z.string(),
|
|
948
988
|
group_id: z.string(),
|
|
949
989
|
question: z.string().default(''),
|
|
950
990
|
answer_format: answerFormat,
|
|
951
|
-
// Format-specific payload: select options, form JSON schema
|
|
952
|
-
// wire sends `null` for formats that carry
|
|
953
|
-
// normalized to `{}` so consumers always see an object.
|
|
991
|
+
// Format-specific payload: select options, form JSON schema (with optional per-send
|
|
992
|
+
// `data` and `pages`), external url. The wire sends `null` for formats that carry
|
|
993
|
+
// none (text/confirm); it is normalized to `{}` so consumers always see an object.
|
|
954
994
|
format_payload: z
|
|
955
995
|
.record(z.string(), z.unknown())
|
|
956
996
|
.nullish()
|
|
@@ -991,6 +1031,18 @@ export const interactionAnswered = z.object({
|
|
|
991
1031
|
});
|
|
992
1032
|
export type InteractionAnswered = z.infer<typeof interactionAnswered>;
|
|
993
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* The cancel door's response — a pending ask was WITHDRAWN without an answer (the
|
|
1036
|
+
* asking flow does not resume). `status` is `"cancelled"`; the shape mirrors the
|
|
1037
|
+
* answer door's terminal reply, but the door is distinct so it carries its own
|
|
1038
|
+
* schema.
|
|
1039
|
+
*/
|
|
1040
|
+
export const interactionCancelled = z.object({
|
|
1041
|
+
interaction_id: z.string(),
|
|
1042
|
+
status: z.string(),
|
|
1043
|
+
});
|
|
1044
|
+
export type InteractionCancelled = z.infer<typeof interactionCancelled>;
|
|
1045
|
+
|
|
994
1046
|
/**
|
|
995
1047
|
* A page of pending interactions from `GET /api/interactions?page=&pageSize=`. Each
|
|
996
1048
|
* item is the same record the stream's `interaction.add` frame carries. The pending
|
|
@@ -1456,6 +1508,20 @@ export const triggerAuth = z.enum([
|
|
|
1456
1508
|
]);
|
|
1457
1509
|
export type TriggerAuth = z.infer<typeof triggerAuth>;
|
|
1458
1510
|
|
|
1511
|
+
/**
|
|
1512
|
+
* The optional state subject a hook fire targets (mirrors
|
|
1513
|
+
* `tai42_contract.hooks.HookSubject`): the conversation-target scope plus the subject
|
|
1514
|
+
* `kind` and a `key_expr` jq evaluated over the event payload at fire. `null` leaves the
|
|
1515
|
+
* fire with no ambient state context.
|
|
1516
|
+
*/
|
|
1517
|
+
export const hookSubject = z.object({
|
|
1518
|
+
target_kind: conversationTargetKind,
|
|
1519
|
+
target_name: z.string(),
|
|
1520
|
+
kind: z.string(),
|
|
1521
|
+
key_expr: z.string(),
|
|
1522
|
+
});
|
|
1523
|
+
export type HookSubject = z.infer<typeof hookSubject>;
|
|
1524
|
+
|
|
1459
1525
|
export const hookParams = z.object({
|
|
1460
1526
|
name: z.string(),
|
|
1461
1527
|
topic: z.string(),
|
|
@@ -1463,6 +1529,9 @@ export const hookParams = z.object({
|
|
|
1463
1529
|
// The api-key `user_id` the fire runs AS.
|
|
1464
1530
|
execution_key: z.string().min(1),
|
|
1465
1531
|
tool_kwargs: z.record(z.string(), z.unknown()).default({}),
|
|
1532
|
+
// The optional state subject the fire targets; `null` leaves it with no ambient
|
|
1533
|
+
// state context (a state tool it calls must then carry an explicit subject).
|
|
1534
|
+
subject: hookSubject.nullable().default(null),
|
|
1466
1535
|
condition: z.string().nullable().default(null),
|
|
1467
1536
|
condition_id: z.string().nullable().default(null),
|
|
1468
1537
|
// The `*_kwargs` pair is non-optional in the contract (defaults to {}, never
|
|
@@ -2541,3 +2610,276 @@ export type MarketplaceUninstallResult = z.infer<typeof marketplaceUninstallResu
|
|
|
2541
2610
|
// Background tool-run schemas live in their own module; re-exported here so the
|
|
2542
2611
|
// shared fixture-validation contract resolves them by name alongside the rest.
|
|
2543
2612
|
export { toolRunSubmitResult, toolRunRecord, toolRunList } from './tool-runs';
|
|
2613
|
+
|
|
2614
|
+
// -- states ------------------------------------------------------------------
|
|
2615
|
+
// Shapes mirror tai42_contract.states.models: a declared JSON document, one per
|
|
2616
|
+
// subject. The base document, module fragments, write regimes and effective schema
|
|
2617
|
+
// are permissive JSON records — the auto-form and JsonTree interpret them at runtime;
|
|
2618
|
+
// a drift throws ApiSchemaError. Fields that WP-3's router is finishing concurrently
|
|
2619
|
+
// (`effective_schema`, `regimes`, list/stats derived columns) are modelled optional so
|
|
2620
|
+
// a server that has not yet composed them still parses.
|
|
2621
|
+
|
|
2622
|
+
/** One addressed subject: the conversation-target scope plus the (kind, key) within it. */
|
|
2623
|
+
export const stateSubject = z.object({
|
|
2624
|
+
target_kind: conversationTargetKind,
|
|
2625
|
+
target_name: z.string(),
|
|
2626
|
+
kind: z.string(),
|
|
2627
|
+
key: z.string(),
|
|
2628
|
+
});
|
|
2629
|
+
export type StateSubject = z.infer<typeof stateSubject>;
|
|
2630
|
+
|
|
2631
|
+
/** One absolute write-regime rule composed over the mounts: `{path, regime}`, permissive. */
|
|
2632
|
+
export const stateRegime = z.record(z.string(), z.unknown());
|
|
2633
|
+
|
|
2634
|
+
/**
|
|
2635
|
+
* A declared state served on every read. `schema` is the base JSON Schema;
|
|
2636
|
+
* `effective_schema` (the base composed with every mounted module's fragment) and
|
|
2637
|
+
* `regimes` (the absolute write-regime rules) are platform-COMPUTED and served on every
|
|
2638
|
+
* read — modelled optional so a write body that omits them, or a server yet to compose
|
|
2639
|
+
* them, still parses. A drift throws ApiSchemaError.
|
|
2640
|
+
*/
|
|
2641
|
+
export const stateDeclaration = z.object({
|
|
2642
|
+
name: z.string(),
|
|
2643
|
+
description: z.string().default(''),
|
|
2644
|
+
schema: jsonSchema,
|
|
2645
|
+
subject_kinds: z.array(z.string()),
|
|
2646
|
+
default_subject_kind: z.string(),
|
|
2647
|
+
retention_days: z.number().nullable().default(null),
|
|
2648
|
+
effective_schema: jsonSchema.nullable().optional(),
|
|
2649
|
+
regimes: z.array(stateRegime).nullable().optional(),
|
|
2650
|
+
});
|
|
2651
|
+
export type StateDeclaration = z.infer<typeof stateDeclaration>;
|
|
2652
|
+
|
|
2653
|
+
/**
|
|
2654
|
+
* One row of `GET /api/states` — a served declaration (`list_states` dumps every
|
|
2655
|
+
* `StateDeclaration`) plus `updated_at` (the row's last-write timestamp, ISO or
|
|
2656
|
+
* `null`). The list carries no record count; the `Records` column reads
|
|
2657
|
+
* `getStateStats` lazily per row.
|
|
2658
|
+
*/
|
|
2659
|
+
export const stateListItem = stateDeclaration.extend({
|
|
2660
|
+
updated_at: z.string().nullable().default(null),
|
|
2661
|
+
});
|
|
2662
|
+
export type StateListItem = z.infer<typeof stateListItem>;
|
|
2663
|
+
export const stateList = z.array(stateListItem);
|
|
2664
|
+
|
|
2665
|
+
/** One module mount on a state: where the fragment lands + its param/declaration values. */
|
|
2666
|
+
export const stateMount = z.object({
|
|
2667
|
+
module: z.string(),
|
|
2668
|
+
path: z.array(z.string()),
|
|
2669
|
+
parameters: z.record(z.string(), z.unknown()).default({}),
|
|
2670
|
+
declarations: z.record(z.string(), z.unknown()).default({}),
|
|
2671
|
+
});
|
|
2672
|
+
export type StateMount = z.infer<typeof stateMount>;
|
|
2673
|
+
export const stateMountList = z.array(stateMount);
|
|
2674
|
+
|
|
2675
|
+
/** `GET /api/states/{name}` — the declaration (with effective schema + regimes) plus mounts. */
|
|
2676
|
+
export const stateDetail = stateDeclaration.extend({
|
|
2677
|
+
mounts: z.array(stateMount).default([]),
|
|
2678
|
+
});
|
|
2679
|
+
export type StateDetail = z.infer<typeof stateDetail>;
|
|
2680
|
+
|
|
2681
|
+
/**
|
|
2682
|
+
* `GET /api/states/{name}/stats` — `records` (total documents), `per_field` (the count
|
|
2683
|
+
* of records carrying each base-schema property), `per_kind` (records per subject kind)
|
|
2684
|
+
* and `consumers` (the count of listable consumers). The Records tab shows `records`;
|
|
2685
|
+
* the states list reads it lazily for its Records column.
|
|
2686
|
+
*/
|
|
2687
|
+
export const stateStats = z.object({
|
|
2688
|
+
records: z.number(),
|
|
2689
|
+
per_field: z.record(z.string(), z.number()).default({}),
|
|
2690
|
+
per_kind: z.record(z.string(), z.number()).default({}),
|
|
2691
|
+
consumers: z.number().default(0),
|
|
2692
|
+
});
|
|
2693
|
+
export type StateStats = z.infer<typeof stateStats>;
|
|
2694
|
+
|
|
2695
|
+
/** The platform half of a state-module document (mirrors StateModuleDocument). */
|
|
2696
|
+
export const stateModuleDocument = z.object({
|
|
2697
|
+
kind: z.literal('state-module').default('state-module'),
|
|
2698
|
+
name: z.string(),
|
|
2699
|
+
description: z.string().default(''),
|
|
2700
|
+
parameters: z.record(z.string(), z.unknown()).default({}),
|
|
2701
|
+
schema: jsonSchema,
|
|
2702
|
+
regimes: z.array(stateRegime).default([]),
|
|
2703
|
+
declarations: z.record(z.string(), z.unknown()).nullable().default(null),
|
|
2704
|
+
trace: z.record(z.string(), z.unknown()).default({}),
|
|
2705
|
+
});
|
|
2706
|
+
export type StateModuleDocument = z.infer<typeof stateModuleDocument>;
|
|
2707
|
+
|
|
2708
|
+
/**
|
|
2709
|
+
* One row of `GET /api/state-modules`. The module document plus the two derived columns
|
|
2710
|
+
* the Modules tab shows: `mounted_on` (how many states mount it — a delete is refused
|
|
2711
|
+
* while > 0) and `shipped_default` (a platform-shipped module). ASSUMED derived fields
|
|
2712
|
+
* (WP-3 owns the list router); the document fields are verbatim.
|
|
2713
|
+
*/
|
|
2714
|
+
export const stateModuleListItem = stateModuleDocument.extend({
|
|
2715
|
+
mounted_on: z.number().default(0),
|
|
2716
|
+
shipped_default: z.boolean().default(false),
|
|
2717
|
+
});
|
|
2718
|
+
export type StateModuleListItem = z.infer<typeof stateModuleListItem>;
|
|
2719
|
+
export const stateModuleList = z.array(stateModuleListItem);
|
|
2720
|
+
|
|
2721
|
+
/** A read record: the state, its subject, the document + monotonic seq, and any fold. */
|
|
2722
|
+
export const recordView = z.object({
|
|
2723
|
+
state: z.string(),
|
|
2724
|
+
subject: stateSubject,
|
|
2725
|
+
data: z.record(z.string(), z.unknown()).default({}),
|
|
2726
|
+
seq: z.number(),
|
|
2727
|
+
canonical_subject: stateSubject,
|
|
2728
|
+
folded_from: z.array(stateSubject).default([]),
|
|
2729
|
+
});
|
|
2730
|
+
export type RecordView = z.infer<typeof recordView>;
|
|
2731
|
+
|
|
2732
|
+
/** The outcome of an `apply` (a delta batch): whether it applied + the resulting doc. */
|
|
2733
|
+
export const applyResult = z.object({
|
|
2734
|
+
applied: z.boolean(),
|
|
2735
|
+
data: z.record(z.string(), z.unknown()).nullable().default(null),
|
|
2736
|
+
seq: z.number().nullable().default(null),
|
|
2737
|
+
skipped: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
2738
|
+
});
|
|
2739
|
+
export type ApplyResult = z.infer<typeof applyResult>;
|
|
2740
|
+
|
|
2741
|
+
/**
|
|
2742
|
+
* One paged subject row: the addressed `subject` plus its record's last-write
|
|
2743
|
+
* `updated_at` — epoch seconds (the store's `extract(epoch FROM updated_at)`), served
|
|
2744
|
+
* raw (no `mode="json"` dump), and `NOT NULL` on every record, so a plain number.
|
|
2745
|
+
*/
|
|
2746
|
+
export const subjectRow = z.object({
|
|
2747
|
+
subject: stateSubject,
|
|
2748
|
+
updated_at: z.number(),
|
|
2749
|
+
});
|
|
2750
|
+
export type SubjectRow = z.infer<typeof subjectRow>;
|
|
2751
|
+
|
|
2752
|
+
/**
|
|
2753
|
+
* `GET /api/states/{name}/subjects` — a keyset page of subject rows (optionally of one
|
|
2754
|
+
* `kind`): the matched `subjects` plus the `next_cursor` a "Load more" carries back
|
|
2755
|
+
* (`null` at the end).
|
|
2756
|
+
*/
|
|
2757
|
+
export const subjectPage = z.object({
|
|
2758
|
+
subjects: z.array(subjectRow),
|
|
2759
|
+
next_cursor: z.string().nullable().default(null),
|
|
2760
|
+
});
|
|
2761
|
+
export type SubjectPage = z.infer<typeof subjectPage>;
|
|
2762
|
+
|
|
2763
|
+
/**
|
|
2764
|
+
* `POST /api/states/{name}/records/search` — a keyset page of the subject rows whose
|
|
2765
|
+
* document contains the filters (a JSONB containment match). Each hit's `subject` opens
|
|
2766
|
+
* the record page; `next_cursor` pages the rest.
|
|
2767
|
+
*/
|
|
2768
|
+
export const recordSearchPage = z.object({
|
|
2769
|
+
matches: z.array(subjectRow),
|
|
2770
|
+
next_cursor: z.string().nullable().default(null),
|
|
2771
|
+
});
|
|
2772
|
+
export type RecordSearchPage = z.infer<typeof recordSearchPage>;
|
|
2773
|
+
|
|
2774
|
+
/** A WriteEntry's origin completed by the platform chokepoint (mirrors CompletedOrigin). */
|
|
2775
|
+
export const completedOrigin = z.object({
|
|
2776
|
+
consumer: z.string().nullable().default(null),
|
|
2777
|
+
// Opaque consumer-supplied provenance JSON the platform stores as is.
|
|
2778
|
+
meta: z.record(z.string(), z.unknown()).nullable().default(null),
|
|
2779
|
+
run_id: z.string().nullable().default(null),
|
|
2780
|
+
op_id: z.string().nullable().default(null),
|
|
2781
|
+
door: z.string(),
|
|
2782
|
+
actor: z.string().nullable().default(null),
|
|
2783
|
+
turn_id: z.string().nullable().default(null),
|
|
2784
|
+
inbound_id: z.string().nullable().default(null),
|
|
2785
|
+
});
|
|
2786
|
+
export type CompletedOrigin = z.infer<typeof completedOrigin>;
|
|
2787
|
+
|
|
2788
|
+
/** One row of a subject's audit trail (mirrors WriteEntry). `paths` are absolute. */
|
|
2789
|
+
export const writeEntry = z.object({
|
|
2790
|
+
seq: z.number(),
|
|
2791
|
+
at: z.string(),
|
|
2792
|
+
origin: completedOrigin,
|
|
2793
|
+
paths: z.array(z.array(z.union([z.string(), z.number()]))).default([]),
|
|
2794
|
+
});
|
|
2795
|
+
export type WriteEntry = z.infer<typeof writeEntry>;
|
|
2796
|
+
|
|
2797
|
+
/** `GET /api/states/{name}/records/.../writes` — a keyset page of the audit trail. */
|
|
2798
|
+
export const writesPage = z.object({
|
|
2799
|
+
items: z.array(writeEntry),
|
|
2800
|
+
next_cursor: z.string().nullable().default(null),
|
|
2801
|
+
});
|
|
2802
|
+
export type WritesPage = z.infer<typeof writesPage>;
|
|
2803
|
+
|
|
2804
|
+
/** Where the Studio opens a consumer (mirrors ConsumerLink): a token+search OR plugin path. */
|
|
2805
|
+
export const consumerLink = z.object({
|
|
2806
|
+
token: z.string().nullable().default(null),
|
|
2807
|
+
plugin_path: z.string().nullable().default(null),
|
|
2808
|
+
search: z.record(z.string(), z.unknown()).nullable().default(null),
|
|
2809
|
+
});
|
|
2810
|
+
export type ConsumerLink = z.infer<typeof consumerLink>;
|
|
2811
|
+
|
|
2812
|
+
/**
|
|
2813
|
+
* One thing that binds a state (mirrors ConsumerRow): its `kind` (flow / hook / schedule
|
|
2814
|
+
* / agent), `name`, human `detail` and optional `link`. `unavailable` marks a consumer
|
|
2815
|
+
* family that cannot be listed on this deployment (e.g. no scheduling backend), surfaced
|
|
2816
|
+
* as a muted line — never swallowed.
|
|
2817
|
+
*/
|
|
2818
|
+
export const consumerRow = z.object({
|
|
2819
|
+
kind: z.string(),
|
|
2820
|
+
name: z.string().nullable().default(null),
|
|
2821
|
+
detail: z.string().nullable().default(null),
|
|
2822
|
+
link: consumerLink.nullable().default(null),
|
|
2823
|
+
unavailable: z.string().nullable().default(null),
|
|
2824
|
+
});
|
|
2825
|
+
export type ConsumerRow = z.infer<typeof consumerRow>;
|
|
2826
|
+
|
|
2827
|
+
/** `GET /api/states/{name}/consumers` — the union of every registered lister's rows. */
|
|
2828
|
+
export const stateConsumers = z.array(consumerRow);
|
|
2829
|
+
export type StateConsumers = z.infer<typeof stateConsumers>;
|
|
2830
|
+
|
|
2831
|
+
/** `DELETE /api/states/{name}` — the removed state. */
|
|
2832
|
+
export const stateDeleted = z.object({ name: z.string(), deleted: z.literal(true) });
|
|
2833
|
+
|
|
2834
|
+
/** `PUT /api/states/{name}/mounts/{module}` — the module now mounted on the state. */
|
|
2835
|
+
export const stateMounted = z.object({
|
|
2836
|
+
mounted: z.literal(true),
|
|
2837
|
+
state: z.string(),
|
|
2838
|
+
module: z.string(),
|
|
2839
|
+
});
|
|
2840
|
+
export type StateMounted = z.infer<typeof stateMounted>;
|
|
2841
|
+
|
|
2842
|
+
/** `PATCH /api/states/{name}/mounts/{module}` — the mount whose declarations were rewritten. */
|
|
2843
|
+
export const stateMountUpdated = z.object({
|
|
2844
|
+
updated: z.literal(true),
|
|
2845
|
+
state: z.string(),
|
|
2846
|
+
module: z.string(),
|
|
2847
|
+
});
|
|
2848
|
+
export type StateMountUpdated = z.infer<typeof stateMountUpdated>;
|
|
2849
|
+
|
|
2850
|
+
/** `DELETE /api/states/{name}/mounts/{module}` — the module unmounted from the state. */
|
|
2851
|
+
export const stateUnmounted = z.object({
|
|
2852
|
+
unmounted: z.literal(true),
|
|
2853
|
+
state: z.string(),
|
|
2854
|
+
module: z.string(),
|
|
2855
|
+
});
|
|
2856
|
+
export type StateUnmounted = z.infer<typeof stateUnmounted>;
|
|
2857
|
+
|
|
2858
|
+
/**
|
|
2859
|
+
* `POST /api/states/{name}/records/.../fold` — the fold report: the `mode`, the
|
|
2860
|
+
* `{kind, key}` of the folded `from` and surviving `into` subjects, whether the fold
|
|
2861
|
+
* was `already` in place, and how many records were `flattened` into the survivor.
|
|
2862
|
+
*/
|
|
2863
|
+
export const stateFoldReport = z.object({
|
|
2864
|
+
mode: z.enum(['switch', 'merge']),
|
|
2865
|
+
from: z.object({ kind: z.string(), key: z.string() }),
|
|
2866
|
+
into: z.object({ kind: z.string(), key: z.string() }),
|
|
2867
|
+
already: z.boolean(),
|
|
2868
|
+
flattened: z.number().default(0),
|
|
2869
|
+
});
|
|
2870
|
+
export type StateFoldReport = z.infer<typeof stateFoldReport>;
|
|
2871
|
+
|
|
2872
|
+
/** `DELETE /api/state-modules/{name}` — the removed module document. */
|
|
2873
|
+
export const stateModuleDeleted = z.object({ name: z.string(), deleted: z.literal(true) });
|
|
2874
|
+
|
|
2875
|
+
/** `DELETE /api/states/{name}/records/...` — the erased record marker. */
|
|
2876
|
+
export const recordErased = z.object({ erased: z.literal(true) });
|
|
2877
|
+
|
|
2878
|
+
/**
|
|
2879
|
+
* `POST /api/state-retention/prune` — the retention sweep's per-state deleted counts
|
|
2880
|
+
* (`{}` when nothing expired); the UI sums the values for its total.
|
|
2881
|
+
*/
|
|
2882
|
+
export const stateRetentionPruned = z.object({
|
|
2883
|
+
pruned: z.record(z.string(), z.number()).default({}),
|
|
2884
|
+
});
|
|
2885
|
+
export type StateRetentionPruned = z.infer<typeof stateRetentionPruned>;
|