@tai42/api-client 12.0.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/dist/client.d.ts +309 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +80 -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 +478 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +270 -3
- package/dist/schemas.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +180 -0
- package/src/index.ts +34 -0
- package/src/schemas.ts +333 -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.
|
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,
|
|
@@ -125,6 +154,10 @@ export type {
|
|
|
125
154
|
InteractionCancelled,
|
|
126
155
|
InteractionsPage,
|
|
127
156
|
InteractionMediaItem,
|
|
157
|
+
FormOption,
|
|
158
|
+
FormData,
|
|
159
|
+
FormPage,
|
|
160
|
+
FormPages,
|
|
128
161
|
MediaKind,
|
|
129
162
|
AnswerFormat,
|
|
130
163
|
Channels,
|
|
@@ -164,6 +197,7 @@ export type {
|
|
|
164
197
|
Notifications,
|
|
165
198
|
ChannelTemplate,
|
|
166
199
|
HookParams,
|
|
200
|
+
HookSubject,
|
|
167
201
|
HookList,
|
|
168
202
|
TriggerAuth,
|
|
169
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()
|
|
@@ -1468,6 +1508,20 @@ export const triggerAuth = z.enum([
|
|
|
1468
1508
|
]);
|
|
1469
1509
|
export type TriggerAuth = z.infer<typeof triggerAuth>;
|
|
1470
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
|
+
|
|
1471
1525
|
export const hookParams = z.object({
|
|
1472
1526
|
name: z.string(),
|
|
1473
1527
|
topic: z.string(),
|
|
@@ -1475,6 +1529,9 @@ export const hookParams = z.object({
|
|
|
1475
1529
|
// The api-key `user_id` the fire runs AS.
|
|
1476
1530
|
execution_key: z.string().min(1),
|
|
1477
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),
|
|
1478
1535
|
condition: z.string().nullable().default(null),
|
|
1479
1536
|
condition_id: z.string().nullable().default(null),
|
|
1480
1537
|
// The `*_kwargs` pair is non-optional in the contract (defaults to {}, never
|
|
@@ -2553,3 +2610,276 @@ export type MarketplaceUninstallResult = z.infer<typeof marketplaceUninstallResu
|
|
|
2553
2610
|
// Background tool-run schemas live in their own module; re-exported here so the
|
|
2554
2611
|
// shared fixture-validation contract resolves them by name alongside the rest.
|
|
2555
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>;
|