@kottera/tera-web 0.1.0-rc.2 → 0.1.0-rc.4

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.
@@ -0,0 +1,196 @@
1
+ /**
2
+ * An ended share.
3
+ *
4
+ * The result of api.v1.shares.delete and api.v1.shares.leave: what the act ended. An ended share decides nothing and is not listed.
5
+ *
6
+ * Schema: sdk/shares/schema/v1/ended.json
7
+ */
8
+ export type SharesEnded = {
9
+ /** The ended share's id. */
10
+ id: string;
11
+ /** The installation whose resource was shared, <vault>/plugins/<installation>. */
12
+ installation: string;
13
+ /** The resource that was shared. */
14
+ resource: string;
15
+ /** The grantee person's id. */
16
+ grantee: string;
17
+ /** The sharer person's id. */
18
+ sharer: string;
19
+ /** How the share ended, as its record holds it: revoked or left. */
20
+ ended: "revoked" | "left";
21
+ };
22
+ /**
23
+ * Extend a share.
24
+ *
25
+ * The params of api.v1.shares.patch, PATCH /api/v1/shares/{id} (ADR-174 D11).
26
+ *
27
+ * Schema: sdk/shares/schema/v1/extend.request.json
28
+ */
29
+ export type SharesExtendRequest = {
30
+ /** The share's id, a UUIDv7. */
31
+ id: string;
32
+ /** The new expiry: later than the share's, at most the installation's max_expiry from now. Absent takes the kind's default_expiry from now, clamped to max_expiry. */
33
+ expires_at?: string;
34
+ };
35
+ /**
36
+ * Name one share.
37
+ *
38
+ * The params of api.v1.shares.get, api.v1.shares.delete and api.v1.shares.leave: the {id} of /api/v1/shares/{id}.
39
+ *
40
+ * Schema: sdk/shares/schema/v1/id.request.json
41
+ */
42
+ export type SharesIdRequest = {
43
+ /** The share's id, a UUIDv7. */
44
+ id: string;
45
+ };
46
+ /**
47
+ * A list of shares.
48
+ *
49
+ * The result of api.v1.shares.list.
50
+ *
51
+ * Schema: sdk/shares/schema/v1/list.json
52
+ */
53
+ export type SharesList = {
54
+ /** The shares, each with whether it decides now. */
55
+ shares: {
56
+ /** The share's id, a UUIDv7. */
57
+ id: string;
58
+ /** The installation whose resource is shared, <vault>/plugins/<installation>. */
59
+ installation: string;
60
+ /** The resource shared, plugins/<vault>/<installation>/<kind>/<id>. */
61
+ resource: string;
62
+ /** The shared verbs. */
63
+ verbs: string[] | null;
64
+ /** What the grantee is: person. */
65
+ grantee_kind: string;
66
+ /** The grantee person's id. */
67
+ grantee: string;
68
+ /** The id of the person who made the share. */
69
+ sharer: string;
70
+ /** The plugin principal that made the share for its sharer; absent when the sharer made it directly. */
71
+ created_via?: string;
72
+ /** When the share was made. */
73
+ created_at: string;
74
+ /** When the share stops deciding. */
75
+ expires_at: string;
76
+ /** When the share was last extended; absent if never. */
77
+ extended_at?: string;
78
+ /** Whether the share decides at least one of its verbs on the grantee's next request, computed from what decides now. */
79
+ deciding: boolean;
80
+ /** When deciding is false, the one reason why. Absent while the share decides. */
81
+ not_deciding?: "sharer_lost_access" | "sharing_disabled" | "installation_not_standing" | "verb_no_longer_shareable" | "pending_invitation";
82
+ }[] | null;
83
+ };
84
+ /**
85
+ * List shares.
86
+ *
87
+ * The params of api.v1.shares.list, the query of GET /api/v1/shares. At most one member is named; with none, the caller's own live shares as grantee and as sharer.
88
+ *
89
+ * Schema: sdk/shares/schema/v1/list.request.json
90
+ */
91
+ export type SharesListRequest = {
92
+ /** An installation, <vault>/plugins/<name>: every share on it. Needs the shares admin grant (api/v1/shares/admin). */
93
+ installation?: string;
94
+ /** One resource, plugins/<vault>/<installation>/<kind>/<id>: every share on it when the caller's own grants include its share-conferring verb, else the caller's own shares on it. */
95
+ resource?: string;
96
+ /** A grantee's person id; someone else's needs the shares admin grant. */
97
+ grantee?: string;
98
+ /** A sharer's person id; someone else's needs the shares admin grant. */
99
+ sharer?: string;
100
+ };
101
+ /**
102
+ * Make a share.
103
+ *
104
+ * The params of api.v1.shares.create, the body of POST /api/v1/shares (ADR-174 D6).
105
+ *
106
+ * Schema: sdk/shares/schema/v1/make.request.json
107
+ */
108
+ export type SharesMakeRequest = {
109
+ /** The one resource shared, plugins/<vault>/<installation>/<kind>/<id>. */
110
+ resource: string;
111
+ /** The shared verbs: a non-empty subset of the kind's sharing { verbs }, never its share-conferring verb. */
112
+ verbs: string[];
113
+ /** The grantee person's id. Exactly one of grantee and grantee_address names the grantee. */
114
+ grantee?: string;
115
+ /** An email address the directory resolves to the grantee. An address no person holds is invited, and the share is pending until they sign in, on a kind whose effective grantee class is addresses; otherwise it is refused share_grantee_not_admitted. */
116
+ grantee_address?: string;
117
+ /** The share's expiry, at most the installation's max_expiry from now. Absent takes the kind's default_expiry, clamped to max_expiry. */
118
+ expires_at?: string;
119
+ };
120
+ /**
121
+ * The vocabulary of the share acts' refusal codes (sdk/shares.Codes).
122
+ *
123
+ * Every stable code a share act is refused with. A code is never renamed; a new refusal is a new code, so a reader keeps a code it does not know as it arrived. share_not_found answers a share the caller may not see exactly as an id no share has. share_grantee_not_admitted answers an address no person holds on a kind whose effective grantee class is not addresses; on one that is, the address is invited and the share is pending until the invitee signs in.
124
+ *
125
+ * Schema: sdk/shares/schema/v1/refusal-code.json
126
+ */
127
+ export type SharesRefusalCode = "share_sharer_not_a_person" | "share_sharing_disabled" | "share_resource_not_shareable" | "share_resource_unknown" | "share_actor_other_installation" | "share_verb_not_shareable" | "share_verb_confers_sharing" | "share_verb_not_held" | "share_grantee_not_admitted" | "share_guest_cannot_invite" | "share_invitation_refused" | "share_grantee_is_sharer" | "share_expiry_beyond_max" | "share_cap_active" | "share_cap_pending_invitations" | "share_exists" | "share_caller_not_a_person" | "share_not_found" | "share_sharer_lost_access" | "share_expiry_not_later" | "share_admin_required" | "share_list_query" | "shares_unavailable";
128
+ /**
129
+ * A share act refused.
130
+ *
131
+ * A share act tera refused, as the shares client answers it: the route's stable code and its words, unchanged, and the detail the code declares.
132
+ *
133
+ * Schema: sdk/shares/schema/v1/refusal.json
134
+ */
135
+ export type SharesRefusal = {
136
+ /** The route's stable code. A code a reader does not know is kept as it arrived. */
137
+ code: string;
138
+ /** The route's words for a person, unchanged. */
139
+ message: string;
140
+ /** On share_verb_not_held, share_verb_not_shareable and share_verb_confers_sharing, the verb refused. */
141
+ verb?: string;
142
+ /** On share_exists, the id of the share already there. */
143
+ existing?: string;
144
+ /** On share_invitation_refused, why the address was not invited (inviter_limited, email_unavailable, ...). */
145
+ invitation?: string;
146
+ };
147
+ /**
148
+ * A share.
149
+ *
150
+ * One live share, with whether it decides now: the result of api.v1.shares.create, api.v1.shares.get and api.v1.shares.patch, and each member of a list (ADR-174 D14).
151
+ *
152
+ * Schema: sdk/shares/schema/v1/share.json
153
+ */
154
+ export type SharesShare = {
155
+ /** The share's id, a UUIDv7. */
156
+ id: string;
157
+ /** The installation whose resource is shared, <vault>/plugins/<installation>. */
158
+ installation: string;
159
+ /** The resource shared, plugins/<vault>/<installation>/<kind>/<id>. */
160
+ resource: string;
161
+ /** The shared verbs. */
162
+ verbs: string[] | null;
163
+ /** What the grantee is: person. */
164
+ grantee_kind: string;
165
+ /** The grantee person's id. */
166
+ grantee: string;
167
+ /** The id of the person who made the share. */
168
+ sharer: string;
169
+ /** The plugin principal that made the share for its sharer; absent when the sharer made it directly. */
170
+ created_via?: string;
171
+ /** When the share was made. */
172
+ created_at: string;
173
+ /** When the share stops deciding. */
174
+ expires_at: string;
175
+ /** When the share was last extended; absent if never. */
176
+ extended_at?: string;
177
+ /** Whether the share decides at least one of its verbs on the grantee's next request, computed from what decides now. */
178
+ deciding: boolean;
179
+ /** When deciding is false, the one reason why. Absent while the share decides. */
180
+ not_deciding?: "sharer_lost_access" | "sharing_disabled" | "installation_not_standing" | "verb_no_longer_shareable" | "pending_invitation";
181
+ };
182
+ /**
183
+ * Every type this module declares, mapped to the repository path of the schema
184
+ * it was generated from.
185
+ */
186
+ export declare const SHARES_SCHEMAS: {
187
+ readonly SharesEnded: "sdk/shares/schema/v1/ended.json";
188
+ readonly SharesExtendRequest: "sdk/shares/schema/v1/extend.request.json";
189
+ readonly SharesIdRequest: "sdk/shares/schema/v1/id.request.json";
190
+ readonly SharesList: "sdk/shares/schema/v1/list.json";
191
+ readonly SharesListRequest: "sdk/shares/schema/v1/list.request.json";
192
+ readonly SharesMakeRequest: "sdk/shares/schema/v1/make.request.json";
193
+ readonly SharesRefusalCode: "sdk/shares/schema/v1/refusal-code.json";
194
+ readonly SharesRefusal: "sdk/shares/schema/v1/refusal.json";
195
+ readonly SharesShare: "sdk/shares/schema/v1/share.json";
196
+ };
@@ -0,0 +1,17 @@
1
+ // Code generated by cmd/internal/genwebtypes from sdk/shares/schema/v1/; DO NOT EDIT.
2
+ // Regenerate with `make web-types` (ADR-162 D4 item 1).
3
+ /**
4
+ * Every type this module declares, mapped to the repository path of the schema
5
+ * it was generated from.
6
+ */
7
+ export const SHARES_SCHEMAS = {
8
+ SharesEnded: "sdk/shares/schema/v1/ended.json",
9
+ SharesExtendRequest: "sdk/shares/schema/v1/extend.request.json",
10
+ SharesIdRequest: "sdk/shares/schema/v1/id.request.json",
11
+ SharesList: "sdk/shares/schema/v1/list.json",
12
+ SharesListRequest: "sdk/shares/schema/v1/list.request.json",
13
+ SharesMakeRequest: "sdk/shares/schema/v1/make.request.json",
14
+ SharesRefusalCode: "sdk/shares/schema/v1/refusal-code.json",
15
+ SharesRefusal: "sdk/shares/schema/v1/refusal.json",
16
+ SharesShare: "sdk/shares/schema/v1/share.json",
17
+ };
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Schema: sdk/workorder/schema/events/v1/event-kind.workorder.json
7
7
  */
8
- export type WorkOrderEventKind = "cohort.set" | "off_tera.wake" | "relation.set" | "relation.removed" | "off_tera.taken" | "off_tera.progress" | "off_tera.reposted" | "off_tera.late_report" | "off_tera.checks_started" | "created" | "transitioned" | "acknowledged" | "retention_expired" | "edited" | "seat.reclaimed" | "claim.overdue" | "claim.unreadable" | "subject_claim.overridden" | "seat.cooling" | "seat.credential_held" | "member_credential.requested" | "member_credential.settled" | "seat.acquired" | "seat.runner_released" | "seat.meters_voided" | "step.binding" | "session.terminated" | "postcondition.verdict" | "parked_run.unreadable" | "parked_run.reclaimed" | "dispatch.settled_unobserved" | "parked_run.parked" | "parked_run.resumed" | "resume.refused" | "dispatch.salvaged" | "workspace.retired" | "dispatch.routed" | "session.lost" | "activity_partition.pinned" | "session.continuity" | "session.context_compacted" | "upstream.answer_forwarded" | "invocation.checkpoint" | "invocation.guidance" | "invocation.guidance.ack" | "invocation.reply" | "invocation.checkpoint.unreadable" | "invocation.guidance.unreadable" | "config_pin.unreadable" | "config_pin.repinned" | "type.followed" | "observation.scrubbed" | "observation.secret-shape-redacted" | "observation.promoted" | "observation.refuted" | "observation.purged" | "conversation.input_barrier" | "walk_resume.fell_back" | "store.ahead_of_binary" | "artifact.published" | "history.dropped" | "history.archived" | "seat.work_order_released" | "step.settled_late" | "step.reattached" | "relay.unrebuilt";
8
+ export type WorkOrderEventKind = "cohort.set" | "off_tera.wake" | "relation.set" | "relation.removed" | "off_tera.taken" | "off_tera.progress" | "off_tera.reposted" | "off_tera.late_report" | "off_tera.checks_started" | "created" | "transitioned" | "acknowledged" | "retention_expired" | "edited" | "seat.reclaimed" | "claim.overdue" | "claim.unreadable" | "subject_claim.overridden" | "seat.cooling" | "seat.credential_held" | "member_credential.requested" | "member_credential.settled" | "seat.acquired" | "seat.runner_released" | "seat.meters_voided" | "step.binding" | "session.terminated" | "postcondition.verdict" | "parked_run.unreadable" | "parked_run.reclaimed" | "dispatch.settled_unobserved" | "parked_run.parked" | "parked_run.resumed" | "resume.refused" | "dispatch.salvaged" | "workspace.retired" | "dispatch.routed" | "session.lost" | "activity_partition.pinned" | "session.continuity" | "session.context_compacted" | "upstream.answer_forwarded" | "invocation.checkpoint" | "invocation.guidance" | "invocation.guidance.ack" | "invocation.reply" | "invocation.checkpoint.unreadable" | "invocation.guidance.unreadable" | "config_pin.unreadable" | "config_pin.repinned" | "type.followed" | "observation.scrubbed" | "observation.secret-shape-redacted" | "observation.promoted" | "observation.refuted" | "observation.purged" | "conversation.input_barrier" | "walk_resume.fell_back" | "store.ahead_of_binary" | "artifact.published" | "history.dropped" | "history.archived" | "seat.work_order_released" | "step.settled_late" | "step.reattached" | "relay.unrebuilt" | "references.recorded";
9
9
  /**
10
10
  * `workorder` event payload (event/workorder).
11
11
  *
@@ -48,6 +48,8 @@ export type WorkOrderAnswerParams = {
48
48
  replace?: boolean;
49
49
  /** Cross ONE refusal: a select site where this Work Order's live step binding is running a candidate the new pin no longer offers (ADR-130 D5, ratified crossable 2026-09-20). The binding row is RETAINED at the same chain generation and the resume RE-SELECTS from the new candidate set, running the candidate loop against live capacity exactly as a first admission would. It does NOT preserve the candidate the run is on — that candidate is gone, which is why the refusal exists. It lifts nothing else: a capability or operation switch at a fixed site has no candidate to re-select, and a withdrawn `yields_turn` offer is frozen for the life of a pinned conversation (ADR-129 D9). Over a site whose candidate IS still offered, or a fixed site, it is a no-op and says so in the manifest rather than refusing. Only meaningful with `repin`. Authorized against `api/v1/workorders/repin/reselect` IN ADDITION to `api/v1/workorders/repin`. Pass `dry_run` first: the manifest's `would_reselect` names the site, the candidate being given up and the set the resume will choose from. */
50
50
  reselect?: boolean;
51
+ /** The id of the step-up a person confirmed this act with, when the Work Order's type requires one for it (ADR-173 D10): a review verdict, an answer, or an off-tera `complete` / `failed`. Call WITHOUT it first: the call is refused `step_up_required` and its error data names the step-up's `id` and the `url` the person opens to confirm it — show them that url; you cannot confirm it yourself. Then retry the IDENTICAL call with step_up = that id. `step_up_pending` means they have not confirmed yet: wait and retry the same call. A step-up confirms exactly one act with exactly this content, once. */
52
+ step_up?: string;
51
53
  /** The operator's exact prose. Interactive answers become one durable ledger message; other answers are wrapped as {"answer": "<text>"}. Ignored when `payload` is supplied. */
52
54
  text?: string;
53
55
  };
@@ -965,6 +967,13 @@ export type WorkOrderGetResult = {
965
967
  authorized_for?: string;
966
968
  plugin?: string;
967
969
  evidence_class?: string;
970
+ step_up?: {
971
+ id: string;
972
+ method: string;
973
+ anchor?: string;
974
+ at: string;
975
+ };
976
+ display_name?: string;
968
977
  };
969
978
  };
970
979
  question?: {
@@ -1166,6 +1175,18 @@ export type WorkOrderGetResult = {
1166
1175
  observed_at: string;
1167
1176
  volumes?: string[];
1168
1177
  };
1178
+ references?: {
1179
+ entries: {
1180
+ name: string;
1181
+ source: string;
1182
+ repository?: string;
1183
+ canonical_repository?: string;
1184
+ ref?: string;
1185
+ commit?: string;
1186
+ history?: boolean;
1187
+ }[] | null;
1188
+ recorded_at: string;
1189
+ };
1169
1190
  execution?: {
1170
1191
  waiter?: string;
1171
1192
  runner?: string;
@@ -1205,10 +1226,22 @@ export type WorkOrderGetResult = {
1205
1226
  describe?: string;
1206
1227
  caller_is_person?: boolean;
1207
1228
  outcome_provenance?: string;
1229
+ step_up?: {
1230
+ id: string;
1231
+ method: string;
1232
+ anchor?: string;
1233
+ at: string;
1234
+ };
1208
1235
  };
1209
1236
  outcome?: {
1210
1237
  outcome_provenance: string;
1211
1238
  attested_by?: string;
1239
+ step_up?: {
1240
+ id: string;
1241
+ method: string;
1242
+ anchor?: string;
1243
+ at: string;
1244
+ };
1212
1245
  };
1213
1246
  relations: {
1214
1247
  relation: string;
@@ -1773,6 +1806,12 @@ export type WorkOrderListResult = {
1773
1806
  describe?: string;
1774
1807
  caller_is_person?: boolean;
1775
1808
  outcome_provenance?: string;
1809
+ step_up?: {
1810
+ id: string;
1811
+ method: string;
1812
+ anchor?: string;
1813
+ at: string;
1814
+ };
1776
1815
  };
1777
1816
  }[] | null;
1778
1817
  };
@@ -1806,6 +1845,12 @@ export type WorkOrderListOffTeraResult = {
1806
1845
  describe?: string;
1807
1846
  caller_is_person?: boolean;
1808
1847
  outcome_provenance?: string;
1848
+ step_up?: {
1849
+ id: string;
1850
+ method: string;
1851
+ anchor?: string;
1852
+ at: string;
1853
+ };
1809
1854
  };
1810
1855
  take_age_seconds?: number;
1811
1856
  /** On a held entry: the JSON Schema this Work Order was pinned to at its first take, which submit_result grades the record against. Absent on posted and taken entries, and when the type declares none. */
@@ -1828,6 +1873,12 @@ export type WorkOrderListOffTeraResult = {
1828
1873
  describe?: string;
1829
1874
  caller_is_person?: boolean;
1830
1875
  outcome_provenance?: string;
1876
+ step_up?: {
1877
+ id: string;
1878
+ method: string;
1879
+ anchor?: string;
1880
+ at: string;
1881
+ };
1831
1882
  };
1832
1883
  take_age_seconds?: number;
1833
1884
  /** On a held entry: the JSON Schema this Work Order was pinned to at its first take, which submit_result grades the record against. Absent on posted and taken entries, and when the type declares none. */
@@ -1850,6 +1901,12 @@ export type WorkOrderListOffTeraResult = {
1850
1901
  describe?: string;
1851
1902
  caller_is_person?: boolean;
1852
1903
  outcome_provenance?: string;
1904
+ step_up?: {
1905
+ id: string;
1906
+ method: string;
1907
+ anchor?: string;
1908
+ at: string;
1909
+ };
1853
1910
  };
1854
1911
  take_age_seconds?: number;
1855
1912
  /** On a held entry: the JSON Schema this Work Order was pinned to at its first take, which submit_result grades the record against. Absent on posted and taken entries, and when the type declares none. */
@@ -1950,6 +2007,12 @@ export type WorkOrderProgressResult = {
1950
2007
  describe?: string;
1951
2008
  caller_is_person?: boolean;
1952
2009
  outcome_provenance?: string;
2010
+ step_up?: {
2011
+ id: string;
2012
+ method: string;
2013
+ anchor?: string;
2014
+ at: string;
2015
+ };
1953
2016
  } | null;
1954
2017
  addressed_events: {
1955
2018
  id: number;
@@ -2538,6 +2601,8 @@ export type WorkOrderSubmitResultParams = {
2538
2601
  };
2539
2602
  /** The id of the last event you have processed on this Work Order. The response carries the events addressed to you after it (a repost, a cancel, a wake, an answer, posted guidance), at most 32 with `more` set when there were more, plus `newest_event_id`. The server keeps no cursor: omit it, or pass 0, for everything addressed so far. */
2540
2603
  seen_through?: number;
2604
+ /** The id of the step-up a person confirmed this act with, when the Work Order's type requires one for it (ADR-173 D10): a review verdict, an answer, or an off-tera `complete` / `failed`. Call WITHOUT it first: the call is refused `step_up_required` and its error data names the step-up's `id` and the `url` the person opens to confirm it — show them that url; you cannot confirm it yourself. Then retry the IDENTICAL call with step_up = that id. `step_up_pending` means they have not confirmed yet: wait and retry the same call. A step-up confirms exactly one act with exactly this content, once. */
2605
+ step_up?: string;
2541
2606
  };
2542
2607
  /**
2543
2608
  * `work_order.submit_result` result (v1).
@@ -2553,6 +2618,12 @@ export type WorkOrderSubmitResultResult = {
2553
2618
  outcome?: {
2554
2619
  outcome_provenance: string;
2555
2620
  attested_by?: string;
2621
+ step_up?: {
2622
+ id: string;
2623
+ method: string;
2624
+ anchor?: string;
2625
+ at: string;
2626
+ };
2556
2627
  };
2557
2628
  checks?: string;
2558
2629
  addressed_events: {
@@ -2600,6 +2671,12 @@ export type WorkOrderTakeResult = {
2600
2671
  describe?: string;
2601
2672
  caller_is_person?: boolean;
2602
2673
  outcome_provenance?: string;
2674
+ step_up?: {
2675
+ id: string;
2676
+ method: string;
2677
+ anchor?: string;
2678
+ at: string;
2679
+ };
2603
2680
  } | null;
2604
2681
  instructions: string;
2605
2682
  requirements?: string;
@@ -2663,6 +2740,8 @@ export type WorkOrderTransitionParams = {
2663
2740
  replace?: boolean;
2664
2741
  /** Cross ONE refusal: a select site where this Work Order's live step binding is running a candidate the new pin no longer offers (ADR-130 D5, ratified crossable 2026-09-20). The binding row is RETAINED at the same chain generation and the resume RE-SELECTS from the new candidate set, running the candidate loop against live capacity exactly as a first admission would. It does NOT preserve the candidate the run is on — that candidate is gone, which is why the refusal exists. It lifts nothing else: a capability or operation switch at a fixed site has no candidate to re-select, and a withdrawn `yields_turn` offer is frozen for the life of a pinned conversation (ADR-129 D9). Over a site whose candidate IS still offered, or a fixed site, it is a no-op and says so in the manifest rather than refusing. Only meaningful with `repin`. Authorized against `api/v1/workorders/repin/reselect` IN ADDITION to `api/v1/workorders/repin`. Pass `dry_run` first: the manifest's `would_reselect` names the site, the candidate being given up and the set the resume will choose from. */
2665
2742
  reselect?: boolean;
2743
+ /** The id of the step-up a person confirmed this act with, when the Work Order's type requires one for it (ADR-173 D10): a review verdict, an answer, or an off-tera `complete` / `failed`. Call WITHOUT it first: the call is refused `step_up_required` and its error data names the step-up's `id` and the `url` the person opens to confirm it — show them that url; you cannot confirm it yourself. Then retry the IDENTICAL call with step_up = that id. `step_up_pending` means they have not confirmed yet: wait and retry the same call. A step-up confirms exactly one act with exactly this content, once. */
2744
+ step_up?: string;
2666
2745
  /** action=answer only: the operator's answer text. Read work_order.get(id).question first — that is what is being asked. */
2667
2746
  text?: string;
2668
2747
  };
@@ -3090,6 +3169,12 @@ export type WorkOrderTreeResult = {
3090
3169
  outcome?: {
3091
3170
  outcome_provenance: string;
3092
3171
  attested_by?: string;
3172
+ step_up?: {
3173
+ id: string;
3174
+ method: string;
3175
+ anchor?: string;
3176
+ at: string;
3177
+ };
3093
3178
  };
3094
3179
  replaced?: {
3095
3180
  relation: string;
@@ -3314,6 +3399,12 @@ export type WorkOrderTreeResult = {
3314
3399
  outcome?: {
3315
3400
  outcome_provenance: string;
3316
3401
  attested_by?: string;
3402
+ step_up?: {
3403
+ id: string;
3404
+ method: string;
3405
+ anchor?: string;
3406
+ at: string;
3407
+ };
3317
3408
  };
3318
3409
  replaced?: {
3319
3410
  relation: string;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,15 @@
1
- export { ROUTE_PREFIX, routePath } from "./routes.ts";
1
+ export { ROOT_BASE, ROUTE_PREFIX, appBase, routePath, underBase } from "./routes.ts";
2
2
  export { TeraClient, KitRequestError, UNRECOGNIZED_RESPONSE, type FeedOptions, type InvokeVerdict, type KitErrorBody, type TeraClientOptions, type UploadOptions, type UploadProgress, type UploadRequest, type VerbRefusal, } from "./client.ts";
3
+ export { STEP_UP_HEADER, STEP_UP_POLL_MS, STEP_UP_REQUIRED, STEP_UP_RESUME_PARAM, StepUpError, resumedStepUp, stepUpChallengeOf, stepUpPageURL, type StepUpChallenge, type StepUpEnd, type StepUpOptions, type StepUpWindow, } from "./stepup.ts";
3
4
  export { BASE_DELAY_MS, MAX_DELAY_MS, REPLAY_LIMIT, REPLAY_WINDOW_MS, HEARTBEAT_GRACE_MS, REAUTHENTICATE_EVENT, FeedConnection, browserSignals, defaultOrderKey, type Clock, type FeedConnectionOptions, type FeedConnectionPhase, type FeedConnectionState, type FeedFrame, type FeedLoss, type FeedSignal, type FeedSignals, } from "./feed/connection.ts";
4
5
  export { FeedOpenError, SSEParser, fetchOpener, isRefusal, type FeedOpener, type SSEEvent } from "./feed/sse.ts";
5
6
  export type * from "./generated/feed.ts";
6
7
  export type * from "./generated/kit.ts";
8
+ export type * from "./generated/shares.ts";
7
9
  export type * from "./generated/workorder.ts";
8
10
  export type * from "./generated/workorder-events.ts";
9
11
  export { FEED_SCHEMAS } from "./generated/feed.ts";
10
12
  export { KIT_ROUTE_PREFIX, KIT_ROUTE_TABLE, KIT_SCHEMAS, KIT_SIGNOUT_PATH } from "./generated/kit.ts";
13
+ export { SHARES_SCHEMAS } from "./generated/shares.ts";
11
14
  export { WORK_ORDER_SCHEMAS } from "./generated/workorder.ts";
12
15
  export { WORK_ORDER_EVENT_SCHEMAS } from "./generated/workorder-events.ts";
package/dist/index.js CHANGED
@@ -2,11 +2,13 @@
2
2
  //
3
3
  // Framework-free, with no runtime dependencies: it carries no UI component,
4
4
  // because the framework is the author's.
5
- export { ROUTE_PREFIX, routePath } from "./routes.js";
5
+ export { ROOT_BASE, ROUTE_PREFIX, appBase, routePath, underBase } from "./routes.js";
6
6
  export { TeraClient, KitRequestError, UNRECOGNIZED_RESPONSE, } from "./client.js";
7
+ export { STEP_UP_HEADER, STEP_UP_POLL_MS, STEP_UP_REQUIRED, STEP_UP_RESUME_PARAM, StepUpError, resumedStepUp, stepUpChallengeOf, stepUpPageURL, } from "./stepup.js";
7
8
  export { BASE_DELAY_MS, MAX_DELAY_MS, REPLAY_LIMIT, REPLAY_WINDOW_MS, HEARTBEAT_GRACE_MS, REAUTHENTICATE_EVENT, FeedConnection, browserSignals, defaultOrderKey, } from "./feed/connection.js";
8
9
  export { FeedOpenError, SSEParser, fetchOpener, isRefusal } from "./feed/sse.js";
9
10
  export { FEED_SCHEMAS } from "./generated/feed.js";
10
11
  export { KIT_ROUTE_PREFIX, KIT_ROUTE_TABLE, KIT_SCHEMAS, KIT_SIGNOUT_PATH } from "./generated/kit.js";
12
+ export { SHARES_SCHEMAS } from "./generated/shares.js";
11
13
  export { WORK_ORDER_SCHEMAS } from "./generated/workorder.js";
12
14
  export { WORK_ORDER_EVENT_SCHEMAS } from "./generated/workorder-events.js";
package/dist/routes.d.ts CHANGED
@@ -8,3 +8,18 @@ export declare const ROUTE_PREFIX = "/tera/v1/";
8
8
  * collapses the dot segments and encoding does not change them.
9
9
  */
10
10
  export declare function routePath(...segments: string[]): string;
11
+ /** The base of an app served at the root of its origin. */
12
+ export declare const ROOT_BASE = "/";
13
+ /**
14
+ * appBase checks the path an app is served under on its origin and answers it
15
+ * ending in `/`: `/` on a plugin's own origin, `/plugins/<name>/` on a
16
+ * path-based installation. A base that is not a root-absolute path on the
17
+ * page's own origin is refused.
18
+ */
19
+ export declare function appBase(base?: string): string;
20
+ /**
21
+ * underBase places a path the kit names under an app's base: a kit path
22
+ * (`/tera/v1/me`, from {@link routePath}) or a path relative to the base
23
+ * (`_tera/signout`, the kit's `signout_path`) alike.
24
+ */
25
+ export declare function underBase(base: string, path: string): string;
package/dist/routes.js CHANGED
@@ -5,6 +5,12 @@
5
5
  // The route table itself is declared once, in Go, under sdk/plugin/webui, and
6
6
  // generated into ./generated/kit.ts. What is here is how a path is built from
7
7
  // it.
8
+ //
9
+ // The table's paths are the kit's own: tera's proxy serves a plugin either at
10
+ // the root of its own origin or, on a path-based installation, under
11
+ // `/plugins/<name>/` on tera's origin, and strips that prefix before the kit
12
+ // sees a request. A page therefore reaches every route, and tera's sign-out,
13
+ // under its app's base (`/` or `/plugins/<name>/`), which only the page knows.
8
14
  import { KIT_ROUTE_PREFIX } from "./generated/kit.js";
9
15
  /** The prefix every kit route is served under. */
10
16
  export const ROUTE_PREFIX = KIT_ROUTE_PREFIX;
@@ -30,3 +36,25 @@ export function routePath(...segments) {
30
36
  });
31
37
  return ROUTE_PREFIX + parts.join("/");
32
38
  }
39
+ /** The base of an app served at the root of its origin. */
40
+ export const ROOT_BASE = "/";
41
+ /**
42
+ * appBase checks the path an app is served under on its origin and answers it
43
+ * ending in `/`: `/` on a plugin's own origin, `/plugins/<name>/` on a
44
+ * path-based installation. A base that is not a root-absolute path on the
45
+ * page's own origin is refused.
46
+ */
47
+ export function appBase(base = ROOT_BASE) {
48
+ if (!base.startsWith("/") || base.startsWith("//") || /[?#\\]/.test(base)) {
49
+ throw new TypeError(`appBase: ${JSON.stringify(base)} is not a root-absolute path on this origin`);
50
+ }
51
+ return base.endsWith("/") ? base : base + "/";
52
+ }
53
+ /**
54
+ * underBase places a path the kit names under an app's base: a kit path
55
+ * (`/tera/v1/me`, from {@link routePath}) or a path relative to the base
56
+ * (`_tera/signout`, the kit's `signout_path`) alike.
57
+ */
58
+ export function underBase(base, path) {
59
+ return appBase(base) + (path.startsWith("/") ? path.slice(1) : path);
60
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * A person's step-up, from the page's side (ADR-173 D11 step 3).
3
+ *
4
+ * An act the plugin requires the person's confirmation for is refused
5
+ * `step_up_required`: by the plugin's own route, in the kit's envelope with
6
+ * `error.step_up`, or by a Work Order verb, relayed by the invoke route with
7
+ * the challenge in the refusal's `data.step_up`. Either names the step-up's
8
+ * id and the page on tera's origin the person confirms it on. The client then:
9
+ *
10
+ * 1. opens that page in a new window, and when the browser will not open one,
11
+ * navigates to it instead with a `return_to` naming this page, which tera
12
+ * accepts only on the plugin's own origin;
13
+ * 2. polls `GET /tera/v1/step-ups/{id}` until the step-up is satisfied,
14
+ * declined or expired, and closes the window it opened;
15
+ * 3. sends the identical request again with `Tera-Step-Up: <id>` — or, for a
16
+ * Work Order verb, with `step_up` in its params.
17
+ *
18
+ * A step-up confirms one act once: nothing here holds an id past the retry it
19
+ * was confirmed for, except across the navigation fallback, where the id rides
20
+ * back on the `return_to` as {@link STEP_UP_RESUME_PARAM} and is spent by the
21
+ * first request that needs a step-up after the page loads again.
22
+ */
23
+ import type { KitError } from "./generated/kit.ts";
24
+ /** The request header that carries a confirmed step-up's id to the plugin's route. */
25
+ export declare const STEP_UP_HEADER = "Tera-Step-Up";
26
+ /** The code of a refusal that needs the person's confirmation. */
27
+ export declare const STEP_UP_REQUIRED = "step_up_required";
28
+ /**
29
+ * The query parameter the navigation fallback adds to its `return_to`, naming
30
+ * the step-up the person went to confirm, so the page that loads again
31
+ * resumes it rather than beginning another.
32
+ */
33
+ export declare const STEP_UP_RESUME_PARAM = "tera_step_up";
34
+ /** How often the step-up's state is read while the person confirms it, by default. */
35
+ export declare const STEP_UP_POLL_MS = 1000;
36
+ /** What a refusal names of the step-up it needs: the kit envelope's `error.step_up`. */
37
+ export type StepUpChallenge = NonNullable<KitError["error"]["step_up"]>;
38
+ /** The part of a `Window` the client uses: whether the person closed it, and closing it. */
39
+ export interface StepUpWindow {
40
+ readonly closed: boolean;
41
+ close(): void;
42
+ }
43
+ export interface StepUpOptions {
44
+ /** Opens the step-up's page in a new window; `window.open(url, "_blank")` when absent. Answers null when the browser would not open one. */
45
+ openWindow?: (url: string) => StepUpWindow | null;
46
+ /** The page's own URL, which the navigation fallback returns to; `location.href` when absent. */
47
+ pageURL?: () => string;
48
+ /** Waits between reads of the step-up's state; a timer when absent. */
49
+ sleep?: (ms: number) => Promise<void>;
50
+ /** How long to wait between reads; {@link STEP_UP_POLL_MS} when absent. */
51
+ pollIntervalMs?: number;
52
+ }
53
+ /**
54
+ * How a step-up ended without being confirmed:
55
+ * - `declined` — the person said they did not ask for it;
56
+ * - `expired` — nobody confirmed it in time;
57
+ * - `spent` — it confirmed another request already;
58
+ * - `abandoned` — the person closed its window and it is still waiting;
59
+ * - `navigated` — the browser would not open a window, so the page was sent to
60
+ * tera's; it returns here, and the next request that needs a step-up resumes
61
+ * this one.
62
+ */
63
+ export type StepUpEnd = "declined" | "expired" | "spent" | "abandoned" | "navigated";
64
+ /** A step-up the person did not confirm: the request it was for was not sent again. */
65
+ export declare class StepUpError extends Error {
66
+ /** The step-up's id. */
67
+ readonly stepUpId: string;
68
+ /** How it ended. */
69
+ readonly end: StepUpEnd;
70
+ constructor(stepUpId: string, end: StepUpEnd, message: string);
71
+ }
72
+ /**
73
+ * The step-up a refusal needs confirmed, when it is one this client can have
74
+ * confirmed: code `step_up_required` with a page to open. A refusal naming an
75
+ * ended step-up carries no page, and is not one.
76
+ */
77
+ export declare function stepUpChallengeOf(code: string, stepUp: unknown): StepUpChallenge | null;
78
+ /**
79
+ * The step-up page's URL with `return_to` set to `returnTo`, itself carrying
80
+ * {@link STEP_UP_RESUME_PARAM}. tera follows it back only when it is on the
81
+ * plugin's own origin.
82
+ */
83
+ export declare function stepUpPageURL(pageURL: string, stepUpId: string, returnTo: string): string;
84
+ /** The step-up id a page loaded through the navigation fallback carries back, or null. */
85
+ export declare function resumedStepUp(pageURL: string): string | null;
86
+ export declare function defaultOpenWindow(url: string): StepUpWindow | null;
87
+ export declare function defaultSleep(ms: number): Promise<void>;