@oxy.so/contracts 1.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/LICENSE +202 -0
- package/NOTICE +16 -0
- package/dist/cjs/.tsbuildinfo +1 -0
- package/dist/cjs/accountGraph.js +489 -0
- package/dist/cjs/agency.js +439 -0
- package/dist/cjs/browserHub.js +215 -0
- package/dist/cjs/civic.js +163 -0
- package/dist/cjs/commonsSignIn.js +59 -0
- package/dist/cjs/deviceBoot.js +50 -0
- package/dist/cjs/deviceDirectory.js +189 -0
- package/dist/cjs/devicePairing.js +138 -0
- package/dist/cjs/deviceSession.js +164 -0
- package/dist/cjs/emailAgentContext.js +32 -0
- package/dist/cjs/followGraph.js +28 -0
- package/dist/cjs/identity.js +258 -0
- package/dist/cjs/inboxPush.js +24 -0
- package/dist/cjs/index.js +618 -0
- package/dist/cjs/inference/accountBilling.js +334 -0
- package/dist/cjs/inference/aliaModelRelease.js +262 -0
- package/dist/cjs/inference/attribution.js +106 -0
- package/dist/cjs/inference/catalogue.js +487 -0
- package/dist/cjs/inference/entitlement.js +217 -0
- package/dist/cjs/inference/errors.js +309 -0
- package/dist/cjs/inference/identifiers.js +224 -0
- package/dist/cjs/inference/inbox.js +105 -0
- package/dist/cjs/inference/modelDocumentation.js +433 -0
- package/dist/cjs/inference/money.js +188 -0
- package/dist/cjs/inference/priceVersion.js +110 -0
- package/dist/cjs/inference/providerConnection.js +455 -0
- package/dist/cjs/inference/request.js +477 -0
- package/dist/cjs/inference/routingPolicy.js +318 -0
- package/dist/cjs/inference/streamEvents.js +258 -0
- package/dist/cjs/inference/usage.js +329 -0
- package/dist/cjs/inference/version.js +105 -0
- package/dist/cjs/keyRecovery.js +91 -0
- package/dist/cjs/keyRotation.js +75 -0
- package/dist/cjs/links.js +68 -0
- package/dist/cjs/moderationReputation.js +298 -0
- package/dist/cjs/oauth.js +66 -0
- package/dist/cjs/oxyRecordTypes.js +71 -0
- package/dist/cjs/protocol.js +53 -0
- package/dist/cjs/recommendations.js +168 -0
- package/dist/cjs/reputation.js +297 -0
- package/dist/cjs/sessionStatus.js +121 -0
- package/dist/cjs/transparency.js +89 -0
- package/dist/cjs/updates.js +252 -0
- package/dist/cjs/userInvalidation.js +89 -0
- package/dist/cjs/userResponse.js +245 -0
- package/dist/cjs/username.js +290 -0
- package/dist/cjs/webauthn.js +71 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/accountGraph.js +480 -0
- package/dist/esm/agency.js +436 -0
- package/dist/esm/browserHub.js +212 -0
- package/dist/esm/civic.js +160 -0
- package/dist/esm/commonsSignIn.js +56 -0
- package/dist/esm/deviceBoot.js +47 -0
- package/dist/esm/deviceDirectory.js +186 -0
- package/dist/esm/devicePairing.js +135 -0
- package/dist/esm/deviceSession.js +161 -0
- package/dist/esm/emailAgentContext.js +29 -0
- package/dist/esm/followGraph.js +27 -0
- package/dist/esm/identity.js +255 -0
- package/dist/esm/inboxPush.js +21 -0
- package/dist/esm/index.js +172 -0
- package/dist/esm/inference/accountBilling.js +331 -0
- package/dist/esm/inference/aliaModelRelease.js +259 -0
- package/dist/esm/inference/attribution.js +103 -0
- package/dist/esm/inference/catalogue.js +484 -0
- package/dist/esm/inference/entitlement.js +214 -0
- package/dist/esm/inference/errors.js +306 -0
- package/dist/esm/inference/identifiers.js +221 -0
- package/dist/esm/inference/inbox.js +102 -0
- package/dist/esm/inference/modelDocumentation.js +430 -0
- package/dist/esm/inference/money.js +185 -0
- package/dist/esm/inference/priceVersion.js +107 -0
- package/dist/esm/inference/providerConnection.js +452 -0
- package/dist/esm/inference/request.js +474 -0
- package/dist/esm/inference/routingPolicy.js +315 -0
- package/dist/esm/inference/streamEvents.js +255 -0
- package/dist/esm/inference/usage.js +326 -0
- package/dist/esm/inference/version.js +102 -0
- package/dist/esm/keyRecovery.js +88 -0
- package/dist/esm/keyRotation.js +72 -0
- package/dist/esm/links.js +65 -0
- package/dist/esm/moderationReputation.js +295 -0
- package/dist/esm/oauth.js +63 -0
- package/dist/esm/oxyRecordTypes.js +68 -0
- package/dist/esm/protocol.js +50 -0
- package/dist/esm/recommendations.js +165 -0
- package/dist/esm/reputation.js +293 -0
- package/dist/esm/sessionStatus.js +118 -0
- package/dist/esm/transparency.js +86 -0
- package/dist/esm/updates.js +249 -0
- package/dist/esm/userInvalidation.js +85 -0
- package/dist/esm/userResponse.js +240 -0
- package/dist/esm/username.js +283 -0
- package/dist/esm/webauthn.js +68 -0
- package/dist/types/.tsbuildinfo +1 -0
- package/dist/types/accountGraph.d.ts +378 -0
- package/dist/types/agency.d.ts +2162 -0
- package/dist/types/browserHub.d.ts +856 -0
- package/dist/types/civic.d.ts +338 -0
- package/dist/types/commonsSignIn.d.ts +58 -0
- package/dist/types/deviceBoot.d.ts +74 -0
- package/dist/types/deviceDirectory.d.ts +1317 -0
- package/dist/types/devicePairing.d.ts +130 -0
- package/dist/types/deviceSession.d.ts +411 -0
- package/dist/types/emailAgentContext.d.ts +248 -0
- package/dist/types/followGraph.d.ts +150 -0
- package/dist/types/identity.d.ts +402 -0
- package/dist/types/inboxPush.d.ts +30 -0
- package/dist/types/index.d.ts +100 -0
- package/dist/types/inference/accountBilling.d.ts +738 -0
- package/dist/types/inference/aliaModelRelease.d.ts +609 -0
- package/dist/types/inference/attribution.d.ts +176 -0
- package/dist/types/inference/catalogue.d.ts +1618 -0
- package/dist/types/inference/entitlement.d.ts +519 -0
- package/dist/types/inference/errors.d.ts +242 -0
- package/dist/types/inference/identifiers.d.ts +182 -0
- package/dist/types/inference/inbox.d.ts +374 -0
- package/dist/types/inference/modelDocumentation.d.ts +1603 -0
- package/dist/types/inference/money.d.ts +185 -0
- package/dist/types/inference/priceVersion.d.ts +182 -0
- package/dist/types/inference/providerConnection.d.ts +968 -0
- package/dist/types/inference/request.d.ts +2800 -0
- package/dist/types/inference/routingPolicy.d.ts +616 -0
- package/dist/types/inference/streamEvents.d.ts +950 -0
- package/dist/types/inference/usage.d.ts +1164 -0
- package/dist/types/inference/version.d.ts +102 -0
- package/dist/types/keyRecovery.d.ts +138 -0
- package/dist/types/keyRotation.d.ts +103 -0
- package/dist/types/links.d.ts +96 -0
- package/dist/types/moderationReputation.d.ts +487 -0
- package/dist/types/oauth.d.ts +86 -0
- package/dist/types/oxyRecordTypes.d.ts +62 -0
- package/dist/types/protocol.d.ts +86 -0
- package/dist/types/recommendations.d.ts +542 -0
- package/dist/types/reputation.d.ts +457 -0
- package/dist/types/sessionStatus.d.ts +231 -0
- package/dist/types/transparency.d.ts +392 -0
- package/dist/types/updates.d.ts +545 -0
- package/dist/types/userInvalidation.d.ts +94 -0
- package/dist/types/userResponse.d.ts +1706 -0
- package/dist/types/username.d.ts +265 -0
- package/dist/types/webauthn.d.ts +77 -0
- package/package.json +87 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Oxy Trust — the moderation reputation bridge (CrowdSource → Oxy Trust).
|
|
4
|
+
*
|
|
5
|
+
* SINGLE SOURCE OF TRUTH for the wire shapes crossing the one-way boundary
|
|
6
|
+
* between a participatory-moderation service and the Oxy reputation ledger.
|
|
7
|
+
*
|
|
8
|
+
* The direction is not negotiable: a moderation service NEVER writes reputation.
|
|
9
|
+
* It emits an authenticated internal event describing a decision it published,
|
|
10
|
+
* and Oxy's own consequence engine validates that event and derives the effect.
|
|
11
|
+
* Everything in this module is therefore either (a) the event, (b) the receipt
|
|
12
|
+
* the engine returns, or (c) the derived state the engine publishes back to the
|
|
13
|
+
* subject.
|
|
14
|
+
*
|
|
15
|
+
* Design anchors, all load-bearing:
|
|
16
|
+
*
|
|
17
|
+
* - **Conduct is a separate axis from contribution.** A conduct penalty raises
|
|
18
|
+
* `activeRisk` and creates a strike; positive contribution points can never
|
|
19
|
+
* cancel a strike, because standing is derived from active risk and not from
|
|
20
|
+
* the point total. See {@link ReputationConduct}.
|
|
21
|
+
* - **The reporting axis carries only reporting signals.** `abuseScore` on the
|
|
22
|
+
* legacy reliability block conflated rejected reports with every negative
|
|
23
|
+
* transaction; {@link ReputationReporting} exists so a conduct penalty can
|
|
24
|
+
* never inflate a report-abuse figure.
|
|
25
|
+
* - **No binding proof, no effect.** {@link ModerationDecisionEventSubject}
|
|
26
|
+
* requires a `bindingProofId`, and the engine rejects an event whose binding
|
|
27
|
+
* does not resolve to the claimed principal at or before `occurredAt`. An
|
|
28
|
+
* application cannot move a reputation figure by naming a user id.
|
|
29
|
+
* - **One penalty per incident.** The idempotency key is
|
|
30
|
+
* `moderation:<incidentId>:<decisionRevision>:<effectType>`; a hundred
|
|
31
|
+
* reports about the same material produce one effect.
|
|
32
|
+
* - **Every effect carries the policy version it was decided under**, so a
|
|
33
|
+
* consequence can be recomputed under the original policy rather than under
|
|
34
|
+
* whatever the current tuning happens to be.
|
|
35
|
+
*
|
|
36
|
+
* Platform-agnostic — zod only. ESM-safe (no `require()`).
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.applicationModerationTrustSchema = exports.reputationContextualInfluenceSchema = exports.reputationReviewingSchema = exports.reputationReportingSchema = exports.reputationConductSchema = exports.reputationContributionSchema = exports.reputationPersonhoodSchema = exports.identityBindingSchema = exports.registerIdentityBindingSchema = exports.reverseModerationEffectResultSchema = exports.applyModerationDecisionResultSchema = exports.moderationEffectSchema = exports.reverseModerationEffectSchema = exports.finalizeModerationDecisionSchema = exports.moderationDecisionEventSchema = exports.moderationPolicyVersionsSchema = exports.moderationDecisionEventSubjectSchema = exports.moderationFindingSchema = exports.moderationEffectSkipReasonSchema = exports.MODERATION_EFFECT_SKIP_REASONS = exports.applicationModerationStandingSchema = exports.APPLICATION_MODERATION_STANDINGS = exports.identityBindingStatusSchema = exports.IDENTITY_BINDING_STATUSES = exports.identityBindingTypeSchema = exports.IDENTITY_BINDING_TYPES = exports.personhoodStatusSchema = exports.PERSONHOOD_STATUSES = exports.contributionTierSchema = exports.CONTRIBUTION_TIERS = exports.conductStandingSchema = exports.CONDUCT_STANDINGS = exports.conductStrikeStatusSchema = exports.CONDUCT_STRIKE_STATUSES = exports.moderationEffectStatusSchema = exports.MODERATION_EFFECT_STATUSES = exports.moderationEffectTypeSchema = exports.MODERATION_EFFECT_TYPES = exports.moderationDecisionStatusSchema = exports.MODERATION_DECISION_STATUSES = exports.moderationAttributionSchema = exports.MODERATION_ATTRIBUTIONS = exports.moderationFindingScopeSchema = exports.MODERATION_FINDING_SCOPES = exports.moderationSeveritySchema = exports.MODERATION_SEVERITIES = void 0;
|
|
40
|
+
const zod_1 = require("zod");
|
|
41
|
+
/* -------------------------------------------------------------------------- */
|
|
42
|
+
/* Closed value sets */
|
|
43
|
+
/* -------------------------------------------------------------------------- */
|
|
44
|
+
/**
|
|
45
|
+
* Severity band of a moderation finding, lowest → highest.
|
|
46
|
+
*
|
|
47
|
+
* The band — not the taxonomy code — is what the consequence engine consumes:
|
|
48
|
+
* points, active risk and expiry are all keyed by severity in the versioned
|
|
49
|
+
* conduct policy, so a new taxonomy code needs no engine change and no
|
|
50
|
+
* intimate category ever reaches the ledger.
|
|
51
|
+
*/
|
|
52
|
+
exports.MODERATION_SEVERITIES = ['low', 'medium', 'high', 'critical'];
|
|
53
|
+
exports.moderationSeveritySchema = zod_1.z.enum(exports.MODERATION_SEVERITIES);
|
|
54
|
+
/**
|
|
55
|
+
* How far a finding reaches.
|
|
56
|
+
*
|
|
57
|
+
* - `application_local` — the application enforces locally; Oxy Trust is NOT
|
|
58
|
+
* touched. Emitted for completeness; the engine rejects the effect.
|
|
59
|
+
* - `oxy_network` — conduct against the Oxy network as a whole.
|
|
60
|
+
* - `identity_integrity` — impersonation, sybil behaviour, credential abuse.
|
|
61
|
+
*
|
|
62
|
+
* Only `oxy_network` and `identity_integrity` can produce a global effect.
|
|
63
|
+
*/
|
|
64
|
+
exports.MODERATION_FINDING_SCOPES = [
|
|
65
|
+
'application_local',
|
|
66
|
+
'oxy_network',
|
|
67
|
+
'identity_integrity',
|
|
68
|
+
];
|
|
69
|
+
exports.moderationFindingScopeSchema = zod_1.z.enum(exports.MODERATION_FINDING_SCOPES);
|
|
70
|
+
/** Which participant in the reported material the finding attributes to. */
|
|
71
|
+
exports.MODERATION_ATTRIBUTIONS = ['author', 'sharer', 'reporter', 'reviewer'];
|
|
72
|
+
exports.moderationAttributionSchema = zod_1.z.enum(exports.MODERATION_ATTRIBUTIONS);
|
|
73
|
+
/**
|
|
74
|
+
* Lifecycle of the decision the event describes.
|
|
75
|
+
*
|
|
76
|
+
* `inconclusive` is its own outcome and never collapses into "no violation";
|
|
77
|
+
* it simply produces no effect. `superseded` and `corrected` describe a
|
|
78
|
+
* revision that a later one replaced — an event in either state is rejected,
|
|
79
|
+
* because applying it would resurrect a consequence the appeal removed.
|
|
80
|
+
*/
|
|
81
|
+
exports.MODERATION_DECISION_STATUSES = [
|
|
82
|
+
'provisional',
|
|
83
|
+
'final',
|
|
84
|
+
'inconclusive',
|
|
85
|
+
'superseded',
|
|
86
|
+
'corrected',
|
|
87
|
+
];
|
|
88
|
+
exports.moderationDecisionStatusSchema = zod_1.z.enum(exports.MODERATION_DECISION_STATUSES);
|
|
89
|
+
/**
|
|
90
|
+
* The kind of consequence an effect carries. Each is its own axis, and the
|
|
91
|
+
* idempotency key includes it — one incident may legitimately produce a conduct
|
|
92
|
+
* effect for the author AND a report-abuse effect for a malicious reporter.
|
|
93
|
+
*/
|
|
94
|
+
exports.MODERATION_EFFECT_TYPES = [
|
|
95
|
+
'conduct_penalty',
|
|
96
|
+
'report_abuse_penalty',
|
|
97
|
+
'review_abuse_penalty',
|
|
98
|
+
];
|
|
99
|
+
exports.moderationEffectTypeSchema = zod_1.z.enum(exports.MODERATION_EFFECT_TYPES);
|
|
100
|
+
/** Lifecycle of a stored effect. */
|
|
101
|
+
exports.MODERATION_EFFECT_STATUSES = ['applied', 'reversed'];
|
|
102
|
+
exports.moderationEffectStatusSchema = zod_1.z.enum(exports.MODERATION_EFFECT_STATUSES);
|
|
103
|
+
/** Lifecycle of a conduct strike. Only `active` strikes carry active risk. */
|
|
104
|
+
exports.CONDUCT_STRIKE_STATUSES = ['active', 'expired', 'reversed'];
|
|
105
|
+
exports.conductStrikeStatusSchema = zod_1.z.enum(exports.CONDUCT_STRIKE_STATUSES);
|
|
106
|
+
/**
|
|
107
|
+
* Conduct standing, derived from ACTIVE RISK and nothing else.
|
|
108
|
+
*
|
|
109
|
+
* Deliberately independent of the point total: a person may hold a high
|
|
110
|
+
* contribution tier and a `limited` standing at the same time, and earning
|
|
111
|
+
* points cannot move standing back toward `good`. Only expiry or reversal can.
|
|
112
|
+
*/
|
|
113
|
+
exports.CONDUCT_STANDINGS = ['good', 'watch', 'limited', 'restricted'];
|
|
114
|
+
exports.conductStandingSchema = zod_1.z.enum(exports.CONDUCT_STANDINGS);
|
|
115
|
+
/** Contribution tier, derived from contribution points only. */
|
|
116
|
+
exports.CONTRIBUTION_TIERS = ['new', 'trusted', 'high_trust'];
|
|
117
|
+
exports.contributionTierSchema = zod_1.z.enum(exports.CONTRIBUTION_TIERS);
|
|
118
|
+
/** Personhood status. Being a real person proves neither conduct nor competence. */
|
|
119
|
+
exports.PERSONHOOD_STATUSES = ['unknown', 'probable', 'verified'];
|
|
120
|
+
exports.personhoodStatusSchema = zod_1.z.enum(exports.PERSONHOOD_STATUSES);
|
|
121
|
+
/**
|
|
122
|
+
* How an Oxy identity was bound to the actor an application reported.
|
|
123
|
+
*
|
|
124
|
+
* - `oauth_grant` — the user authorized the application through Oxy's own
|
|
125
|
+
* OAuth flow. Oxy wrote the record; the application asserts nothing.
|
|
126
|
+
* - `session_proof` — the application presented the USER'S OWN Oxy access
|
|
127
|
+
* token alongside its service credential, proving the user was present in
|
|
128
|
+
* that application under a named local principal id.
|
|
129
|
+
* - `commons_signature` — a DID-verifiable signature over a server-issued nonce.
|
|
130
|
+
* - `federated_actor` — a resolvable, authorized federated actor link.
|
|
131
|
+
*/
|
|
132
|
+
exports.IDENTITY_BINDING_TYPES = [
|
|
133
|
+
'oauth_grant',
|
|
134
|
+
'session_proof',
|
|
135
|
+
'commons_signature',
|
|
136
|
+
'federated_actor',
|
|
137
|
+
];
|
|
138
|
+
exports.identityBindingTypeSchema = zod_1.z.enum(exports.IDENTITY_BINDING_TYPES);
|
|
139
|
+
/** Binding lifecycle. A revoked binding proves nothing about a later action. */
|
|
140
|
+
exports.IDENTITY_BINDING_STATUSES = ['active', 'revoked'];
|
|
141
|
+
exports.identityBindingStatusSchema = zod_1.z.enum(exports.IDENTITY_BINDING_STATUSES);
|
|
142
|
+
/**
|
|
143
|
+
* An application's own moderation standing. An external application can abuse
|
|
144
|
+
* the system too, so it carries standing exactly like a person does.
|
|
145
|
+
*
|
|
146
|
+
* `sandbox` applications moderate locally and produce NO global effect.
|
|
147
|
+
*/
|
|
148
|
+
exports.APPLICATION_MODERATION_STANDINGS = ['sandbox', 'trusted', 'restricted'];
|
|
149
|
+
exports.applicationModerationStandingSchema = zod_1.z.enum(exports.APPLICATION_MODERATION_STANDINGS);
|
|
150
|
+
/**
|
|
151
|
+
* Why the engine declined to apply an effect.
|
|
152
|
+
*
|
|
153
|
+
* Returned rather than thrown for the cases that are a legitimate outcome of a
|
|
154
|
+
* well-formed event (a sandboxed application, a local-only finding, an
|
|
155
|
+
* inconclusive decision): the emitter must be able to record "delivered, no
|
|
156
|
+
* effect" and stop retrying. Malformed or unauthorized events are HTTP errors,
|
|
157
|
+
* not skip reasons.
|
|
158
|
+
*/
|
|
159
|
+
exports.MODERATION_EFFECT_SKIP_REASONS = [
|
|
160
|
+
'no_binding_proof',
|
|
161
|
+
'binding_after_action',
|
|
162
|
+
'binding_principal_mismatch',
|
|
163
|
+
'binding_revoked',
|
|
164
|
+
'decision_not_effective',
|
|
165
|
+
'decision_superseded',
|
|
166
|
+
'finding_scope_local',
|
|
167
|
+
'finding_not_in_policy',
|
|
168
|
+
'application_not_permitted',
|
|
169
|
+
'no_effective_finding',
|
|
170
|
+
];
|
|
171
|
+
exports.moderationEffectSkipReasonSchema = zod_1.z.enum(exports.MODERATION_EFFECT_SKIP_REASONS);
|
|
172
|
+
exports.moderationFindingSchema = zod_1.z.object({
|
|
173
|
+
code: zod_1.z.string().trim().min(1).max(200),
|
|
174
|
+
severity: exports.moderationSeveritySchema,
|
|
175
|
+
scope: exports.moderationFindingScopeSchema,
|
|
176
|
+
attribution: exports.moderationAttributionSchema,
|
|
177
|
+
family: zod_1.z.string().trim().min(1).max(100),
|
|
178
|
+
});
|
|
179
|
+
exports.moderationDecisionEventSubjectSchema = zod_1.z.object({
|
|
180
|
+
principalType: zod_1.z.literal('oxy_user'),
|
|
181
|
+
principalId: zod_1.z.string().trim().min(1),
|
|
182
|
+
bindingProofId: zod_1.z.string().trim().min(1),
|
|
183
|
+
});
|
|
184
|
+
exports.moderationPolicyVersionsSchema = zod_1.z.object({
|
|
185
|
+
universal: zod_1.z.string().trim().min(1).max(100),
|
|
186
|
+
application: zod_1.z.string().trim().min(1).max(100),
|
|
187
|
+
oxyConduct: zod_1.z.string().trim().min(1).max(100),
|
|
188
|
+
});
|
|
189
|
+
exports.moderationDecisionEventSchema = zod_1.z.object({
|
|
190
|
+
eventId: zod_1.z.string().trim().min(1).max(200),
|
|
191
|
+
reportedApplicationId: zod_1.z.string().trim().min(1).max(200),
|
|
192
|
+
type: zod_1.z.string().trim().min(1).max(200),
|
|
193
|
+
caseId: zod_1.z.string().trim().min(1).max(200),
|
|
194
|
+
incidentId: zod_1.z.string().trim().min(1).max(200),
|
|
195
|
+
decisionId: zod_1.z.string().trim().min(1).max(200),
|
|
196
|
+
decisionRevision: zod_1.z.number().int().min(1),
|
|
197
|
+
subject: exports.moderationDecisionEventSubjectSchema,
|
|
198
|
+
findings: zod_1.z.array(exports.moderationFindingSchema).min(1).max(20),
|
|
199
|
+
decisionStatus: exports.moderationDecisionStatusSchema,
|
|
200
|
+
policyVersions: exports.moderationPolicyVersionsSchema,
|
|
201
|
+
occurredAt: zod_1.z.string().trim().min(1),
|
|
202
|
+
proofHash: zod_1.z.string().trim().min(1).max(200),
|
|
203
|
+
});
|
|
204
|
+
exports.finalizeModerationDecisionSchema = zod_1.z.object({
|
|
205
|
+
decisionId: zod_1.z.string().trim().min(1).max(200),
|
|
206
|
+
decisionRevision: zod_1.z.number().int().min(1),
|
|
207
|
+
});
|
|
208
|
+
exports.reverseModerationEffectSchema = zod_1.z.object({
|
|
209
|
+
decisionId: zod_1.z.string().trim().min(1).max(200),
|
|
210
|
+
decisionRevision: zod_1.z.number().int().min(1),
|
|
211
|
+
reason: zod_1.z.string().trim().min(1).max(500),
|
|
212
|
+
});
|
|
213
|
+
exports.moderationEffectSchema = zod_1.z.object({
|
|
214
|
+
id: zod_1.z.string(),
|
|
215
|
+
incidentId: zod_1.z.string(),
|
|
216
|
+
caseId: zod_1.z.string(),
|
|
217
|
+
decisionId: zod_1.z.string(),
|
|
218
|
+
decisionRevision: zod_1.z.number(),
|
|
219
|
+
principalId: zod_1.z.string(),
|
|
220
|
+
effectType: exports.moderationEffectTypeSchema,
|
|
221
|
+
status: exports.moderationEffectStatusSchema,
|
|
222
|
+
points: zod_1.z.number(),
|
|
223
|
+
activeRisk: zod_1.z.number(),
|
|
224
|
+
severity: exports.moderationSeveritySchema,
|
|
225
|
+
repetitionMultiplier: zod_1.z.number(),
|
|
226
|
+
multiFindingMultiplier: zod_1.z.number(),
|
|
227
|
+
idempotencyKey: zod_1.z.string(),
|
|
228
|
+
transactionId: zod_1.z.string(),
|
|
229
|
+
strikeId: zod_1.z.string().optional(),
|
|
230
|
+
reversalTransactionId: zod_1.z.string().optional(),
|
|
231
|
+
policyVersions: exports.moderationPolicyVersionsSchema,
|
|
232
|
+
appliedAt: zod_1.z.string(),
|
|
233
|
+
reversedAt: zod_1.z.string().optional(),
|
|
234
|
+
});
|
|
235
|
+
exports.applyModerationDecisionResultSchema = zod_1.z.object({
|
|
236
|
+
applied: zod_1.z.boolean(),
|
|
237
|
+
effect: exports.moderationEffectSchema.optional(),
|
|
238
|
+
skipReason: exports.moderationEffectSkipReasonSchema.optional(),
|
|
239
|
+
idempotent: zod_1.z.boolean(),
|
|
240
|
+
});
|
|
241
|
+
exports.reverseModerationEffectResultSchema = zod_1.z.object({
|
|
242
|
+
reversed: zod_1.z.array(exports.moderationEffectSchema),
|
|
243
|
+
idempotent: zod_1.z.boolean(),
|
|
244
|
+
});
|
|
245
|
+
exports.registerIdentityBindingSchema = zod_1.z.object({
|
|
246
|
+
localPrincipalId: zod_1.z.string().trim().min(1).max(200),
|
|
247
|
+
userProofToken: zod_1.z.string().trim().min(1),
|
|
248
|
+
});
|
|
249
|
+
exports.identityBindingSchema = zod_1.z.object({
|
|
250
|
+
id: zod_1.z.string(),
|
|
251
|
+
applicationId: zod_1.z.string(),
|
|
252
|
+
userId: zod_1.z.string(),
|
|
253
|
+
localPrincipalId: zod_1.z.string(),
|
|
254
|
+
bindingType: exports.identityBindingTypeSchema,
|
|
255
|
+
status: exports.identityBindingStatusSchema,
|
|
256
|
+
verifiedAt: zod_1.z.string(),
|
|
257
|
+
createdAt: zod_1.z.string(),
|
|
258
|
+
});
|
|
259
|
+
exports.reputationPersonhoodSchema = zod_1.z.object({
|
|
260
|
+
status: exports.personhoodStatusSchema,
|
|
261
|
+
score: zod_1.z.number(),
|
|
262
|
+
});
|
|
263
|
+
exports.reputationContributionSchema = zod_1.z.object({
|
|
264
|
+
points: zod_1.z.number(),
|
|
265
|
+
tier: exports.contributionTierSchema,
|
|
266
|
+
});
|
|
267
|
+
exports.reputationConductSchema = zod_1.z.object({
|
|
268
|
+
standing: exports.conductStandingSchema,
|
|
269
|
+
activeRisk: zod_1.z.number(),
|
|
270
|
+
activeStrikes: zod_1.z.number(),
|
|
271
|
+
nextExpiryAt: zod_1.z.string().optional(),
|
|
272
|
+
});
|
|
273
|
+
exports.reputationReportingSchema = zod_1.z.object({
|
|
274
|
+
reliability: zod_1.z.number(),
|
|
275
|
+
confidence: zod_1.z.number(),
|
|
276
|
+
confirmed: zod_1.z.number(),
|
|
277
|
+
rejected: zod_1.z.number(),
|
|
278
|
+
malicious: zod_1.z.number(),
|
|
279
|
+
});
|
|
280
|
+
exports.reputationReviewingSchema = zod_1.z.object({
|
|
281
|
+
globalReliability: zod_1.z.number(),
|
|
282
|
+
categoryReliability: zod_1.z.record(zod_1.z.number()),
|
|
283
|
+
languageReliability: zod_1.z.record(zod_1.z.number()),
|
|
284
|
+
});
|
|
285
|
+
exports.reputationContextualInfluenceSchema = zod_1.z.object({
|
|
286
|
+
reportPriorityWeight: zod_1.z.number(),
|
|
287
|
+
reviewSelectionWeight: zod_1.z.number(),
|
|
288
|
+
rankingWeight: zod_1.z.number(),
|
|
289
|
+
});
|
|
290
|
+
exports.applicationModerationTrustSchema = zod_1.z.object({
|
|
291
|
+
applicationId: zod_1.z.string(),
|
|
292
|
+
standing: exports.applicationModerationStandingSchema,
|
|
293
|
+
evidenceIntegrity: zod_1.z.number(),
|
|
294
|
+
identityBindingReliability: zod_1.z.number(),
|
|
295
|
+
decisionOverturnRate: zod_1.z.number(),
|
|
296
|
+
policyQuality: zod_1.z.number(),
|
|
297
|
+
globalReputationEffectsAllowed: zod_1.z.boolean(),
|
|
298
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.oauthAuthorizeCodeResponseSchema = exports.oauthConsentDecisionSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* The two `/auth/oauth/*` responses the browser hub's edge layer reads.
|
|
7
|
+
*
|
|
8
|
+
* They existed on the wire long before this file — `GET /auth/oauth/consent`
|
|
9
|
+
* and `POST /auth/oauth/authorize` are the surface `auth.oxy.so` has always
|
|
10
|
+
* driven with a bearer. What is new (issue #937 Phase 5) is a SECOND consumer
|
|
11
|
+
* that is not the SPA: the IdP's edge layer runs both calls server-side so the
|
|
12
|
+
* device-wide bearer never enters the browser's script context. A shape read by
|
|
13
|
+
* two independently deployed consumers is a contract, so it is written down
|
|
14
|
+
* once here and validated on both sides rather than transcribed into the edge.
|
|
15
|
+
*
|
|
16
|
+
* These are NOT the RFC 6749 token/userinfo responses. Those two speak flat
|
|
17
|
+
* OAuth/OIDC on the wire and are the one place in the API that does not use the
|
|
18
|
+
* `{ data }` envelope; these two are ordinary internal API responses that happen
|
|
19
|
+
* to be about OAuth.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Server-authoritative answer to "must this user be shown a consent screen".
|
|
23
|
+
*
|
|
24
|
+
* Discriminated on `consentRequired` so the two arms cannot be confused by a
|
|
25
|
+
* consumer that reads `reason` first: `trusted`/`granted` are reasons NOT to
|
|
26
|
+
* ask, `new`/`scope_changed` are reasons to ask, and a flat object would let a
|
|
27
|
+
* typo in one produce a plausible value of the other.
|
|
28
|
+
*
|
|
29
|
+
* - `trusted` — the application is first-party/internal/system/official
|
|
30
|
+
* by the REGISTRY's verdict (`isTrustedApplication`), and
|
|
31
|
+
* the request names no scope over the user's own follow
|
|
32
|
+
* graph. Never inferred from a hostname.
|
|
33
|
+
* - `granted` — a prior `AppGrant` already covers every requested scope.
|
|
34
|
+
* - `scope_changed` — a prior grant exists and is missing one.
|
|
35
|
+
* - `new` — no prior grant.
|
|
36
|
+
*
|
|
37
|
+
* `userConsentScopes` names the scopes that FORCED the screen, so the consent UI
|
|
38
|
+
* can say which one it is asking about. Present only on the `true` arm, and only
|
|
39
|
+
* when such a scope exists — a trusted app asked for one is still asked.
|
|
40
|
+
*/
|
|
41
|
+
exports.oauthConsentDecisionSchema = zod_1.z.discriminatedUnion('consentRequired', [
|
|
42
|
+
zod_1.z.object({
|
|
43
|
+
consentRequired: zod_1.z.literal(false),
|
|
44
|
+
reason: zod_1.z.enum(['trusted', 'granted']),
|
|
45
|
+
}),
|
|
46
|
+
zod_1.z.object({
|
|
47
|
+
consentRequired: zod_1.z.literal(true),
|
|
48
|
+
reason: zod_1.z.enum(['new', 'scope_changed']),
|
|
49
|
+
userConsentScopes: zod_1.z.array(zod_1.z.string()).optional(),
|
|
50
|
+
}),
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* A minted authorization code.
|
|
54
|
+
*
|
|
55
|
+
* `state` is echoed back as the caller sent it and is `null` when they sent
|
|
56
|
+
* none — never omitted, so a consumer cannot read "the server dropped my state"
|
|
57
|
+
* as "I sent none". `redirectUri` is echoed for the same reason the code is
|
|
58
|
+
* bound to it server-side: the caller must be able to see that the value the
|
|
59
|
+
* code was issued against is the one it registered.
|
|
60
|
+
*/
|
|
61
|
+
exports.oauthAuthorizeCodeResponseSchema = zod_1.z.object({
|
|
62
|
+
code: zod_1.z.string().min(1),
|
|
63
|
+
state: zod_1.z.string().nullable(),
|
|
64
|
+
redirectUri: zod_1.z.string(),
|
|
65
|
+
expiresIn: zod_1.z.number().int().positive(),
|
|
66
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Oxy-scoped signed-record types.
|
|
4
|
+
*
|
|
5
|
+
* The base `signedRecordEnvelopeSchema` (`./identity`) treats `type` as an OPEN,
|
|
6
|
+
* non-empty string so ANY Oxy app may sign on the shared envelope grammar. The
|
|
7
|
+
* Oxy STORE re-narrows it to the closed set in this module — a `type` outside it
|
|
8
|
+
* is rejected as `invalid_envelope`.
|
|
9
|
+
*
|
|
10
|
+
* `oxySignedRecordTypeSchema` is that runtime gate (the API's `verifyEnvelope`
|
|
11
|
+
* re-narrows with it; the Mongoose `SignedRecord.type` enum and the Postgres
|
|
12
|
+
* CHECK on `signed_records.type` are both derived from `.options`);
|
|
13
|
+
* `OxySignedRecordType` is the matching compile-time union the SDK
|
|
14
|
+
* identity/civic mixins type against.
|
|
15
|
+
*
|
|
16
|
+
* The signing input INCLUDES `type`, so this set is part of the signed bytes —
|
|
17
|
+
* a record cannot have its category swapped after signing, and a value once
|
|
18
|
+
* signed can never be renamed.
|
|
19
|
+
*
|
|
20
|
+
* v1 only ever carried `identity` / `profile` (already in production); v2 added
|
|
21
|
+
* the civic record types (reputation attestations, real-life / peer validations,
|
|
22
|
+
* personhood vouches, verifiable credentials) and the user-node registration
|
|
23
|
+
* record.
|
|
24
|
+
*
|
|
25
|
+
* ## Why `app_record` is here, when it deliberately was not
|
|
26
|
+
*
|
|
27
|
+
* This set used to hold Oxy's own categories only, and said so: an app's `type`
|
|
28
|
+
* was "intentionally NOT in this set". The reason given was that the store
|
|
29
|
+
* accepts only what it knows how to **verify and materialize**. Verification
|
|
30
|
+
* turned out not to argue for the exclusion — the engine verifies a signature
|
|
31
|
+
* against the subject's keys whatever the category says — and materialization
|
|
32
|
+
* is the app's job, not the store's: an app projects its own feed tables from
|
|
33
|
+
* records it reads back.
|
|
34
|
+
*
|
|
35
|
+
* What changed is the decision the exclusion blocked. One chain per PERSON, held
|
|
36
|
+
* by Oxy, is the ecosystem substrate: apps append their records to the subject's
|
|
37
|
+
* one chain instead of each keeping a private chain for the same person. A
|
|
38
|
+
* closed set that admits no app category makes that unrepresentable.
|
|
39
|
+
*
|
|
40
|
+
* `app_record` is ONE value rather than an open lane, and the lexicon lives in
|
|
41
|
+
* the envelope's `collection` (`app.mention.feed.post`, `app.syra.*`), which the
|
|
42
|
+
* store denormalizes to `signed_records.nsid` and indexes. So a new app needs no
|
|
43
|
+
* change here — it picks its own collection namespace and signs `app_record`,
|
|
44
|
+
* exactly as Mention already does in production. Keeping the set closed is what
|
|
45
|
+
* keeps the CHECK a real constraint.
|
|
46
|
+
*
|
|
47
|
+
* **Admitting the category is not the whole of that decision.** Two gates sit
|
|
48
|
+
* beside it and are unchanged: an app record must arrive as a v2 (chained)
|
|
49
|
+
* envelope, and `oxyVerificationResolver` accepts exactly one custodial issuer
|
|
50
|
+
* (`OXY_DID`). So a record a user signs themselves verifies here today, while
|
|
51
|
+
* one an app signs custodially under its OWN issuer DID does not — that needs a
|
|
52
|
+
* separate, deliberate answer about which issuers may write to a person's chain.
|
|
53
|
+
*
|
|
54
|
+
* Platform-agnostic — zod only, no react/react-native/expo, ESM-safe.
|
|
55
|
+
*/
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.oxySignedRecordTypeSchema = void 0;
|
|
58
|
+
const zod_1 = require("zod");
|
|
59
|
+
exports.oxySignedRecordTypeSchema = zod_1.z.enum([
|
|
60
|
+
'identity',
|
|
61
|
+
'profile',
|
|
62
|
+
'reputation_attestation',
|
|
63
|
+
'real_life_attestation',
|
|
64
|
+
'validation_verdict',
|
|
65
|
+
'personhood_vouch',
|
|
66
|
+
'credential',
|
|
67
|
+
'node',
|
|
68
|
+
// Any Oxy app's own record. The LEXICON is the envelope's `collection`, not
|
|
69
|
+
// this value — see the header.
|
|
70
|
+
'app_record',
|
|
71
|
+
]);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generic "Oxy Protocol" record surface — the app-agnostic conventions every app
|
|
4
|
+
* follows to decentralize its own content on the shared signed-record substrate.
|
|
5
|
+
*
|
|
6
|
+
* The base `signedRecordEnvelopeSchema` (`./identity`) is the WIRE grammar: a
|
|
7
|
+
* signed envelope whose `type` is an open string and whose `record` is an opaque
|
|
8
|
+
* `Record<string, unknown>`. An app layers its own LEXICON on top of that
|
|
9
|
+
* grammar — a typed projection of the `record` payload, addressed by an
|
|
10
|
+
* AtProto-style `(collection, rkey)` key — WITHOUT forking the envelope schema.
|
|
11
|
+
*
|
|
12
|
+
* ## Recipe — defining an app lexicon record
|
|
13
|
+
*
|
|
14
|
+
* For each record kind an app wants to publish:
|
|
15
|
+
*
|
|
16
|
+
* 1. Define the `record` PAYLOAD schema as a `z.ZodType<XPayload>` (e.g.
|
|
17
|
+
* `app.mention.feed.post` → `mentionPostRecordSchema: z.ZodType<MentionPost>`).
|
|
18
|
+
* This validates ONLY the inner `record`, not the envelope.
|
|
19
|
+
* 2. Declare the `collection` NSID as a constant (e.g.
|
|
20
|
+
* `export const MENTION_POST_COLLECTION = 'app.mention.feed.post'`).
|
|
21
|
+
* 3. Reuse the UNCHANGED {@link signedRecordEnvelopeSchema} for the envelope. The
|
|
22
|
+
* base treats `record` as `z.record(z.unknown())`, so the app validates the
|
|
23
|
+
* envelope with the base schema first, then parses `envelope.record` with its
|
|
24
|
+
* own payload schema. {@link LexiconRecord} is the typed projection that pairs
|
|
25
|
+
* the `(collection, rkey)` key with the parsed payload.
|
|
26
|
+
*
|
|
27
|
+
* The Oxy civic contracts (`./civic`) already follow this convention implicitly:
|
|
28
|
+
* each civic record (`real_life_attestation`, `personhood_vouch`, `credential`,
|
|
29
|
+
* …) ships a `record`-payload schema and is carried by the base envelope.
|
|
30
|
+
*
|
|
31
|
+
* ## Chain-wire shapes
|
|
32
|
+
*
|
|
33
|
+
* {@link ChainHeadResponse} and {@link LogPageResponse} are the shared response
|
|
34
|
+
* shapes every chain store exposes (Oxy's `GET /identity/records/:userId/chain/head`,
|
|
35
|
+
* `GET /identity/head/:userId`, `GET /identity/log/:userId`, and any app node's
|
|
36
|
+
* equivalents). They are defined ONCE here so producers and consumers (the API
|
|
37
|
+
* handlers, the SDK identity/nodes mixins, app nodes) cannot drift.
|
|
38
|
+
*
|
|
39
|
+
* Platform-agnostic — zod only, no react/react-native/expo, ESM-safe.
|
|
40
|
+
*/
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.logPageResponseSchema = exports.chainHeadResponseSchema = void 0;
|
|
43
|
+
const zod_1 = require("zod");
|
|
44
|
+
const identity_1 = require("./identity");
|
|
45
|
+
exports.chainHeadResponseSchema = zod_1.z.object({
|
|
46
|
+
headRecordId: zod_1.z.string().nullable(),
|
|
47
|
+
seq: zod_1.z.number().int(),
|
|
48
|
+
recordCount: zod_1.z.number().int().nonnegative(),
|
|
49
|
+
});
|
|
50
|
+
exports.logPageResponseSchema = zod_1.z.object({
|
|
51
|
+
records: zod_1.z.array(identity_1.signedRecordEnvelopeSchema),
|
|
52
|
+
count: zod_1.z.number().int().nonnegative(),
|
|
53
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Recommendation-engine API contracts.
|
|
4
|
+
*
|
|
5
|
+
* SINGLE SOURCE OF TRUTH for the wire shape of the reputation-weighted
|
|
6
|
+
* profile-recommendation surface (`POST /profiles/recommendations`) and the
|
|
7
|
+
* cross-app signal-ingest endpoint (`POST /app-signals/ingest`). The API
|
|
8
|
+
* validates its INPUT/OUTPUT against these schemas; consumer SDKs validate the
|
|
9
|
+
* same definitions, so the producer and every consumer cannot drift.
|
|
10
|
+
*
|
|
11
|
+
* Platform-agnostic — zod is the only runtime dependency (no react / react-native
|
|
12
|
+
* / expo, ESM-safe).
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.appAffinityEventsIngestSchema = exports.appAffinityEventSchema = exports.appAffinityEventTypeSchema = exports.appUserSignalIngestSchema = exports.appInterestInputSchema = exports.appEndorsementInputSchema = exports.recommendationResponseSchema = exports.recommendationItemSchema = exports.recommendationCountSchema = exports.recommendationRequestSchema = exports.recommendationSignalWeightsSchema = exports.recommendationBoostSchema = exports.recommendationExcludeTypeSchema = void 0;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
const userResponse_1 = require("./userResponse");
|
|
18
|
+
/** User-type filters a caller may exclude from the recommendation surface. */
|
|
19
|
+
exports.recommendationExcludeTypeSchema = zod_1.z.enum([
|
|
20
|
+
'federated',
|
|
21
|
+
'agent',
|
|
22
|
+
'automated',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* A caller-supplied editorial boost. `userIds` are nudged up (or down, for a
|
|
26
|
+
* negative weight) in the ranking; the optional `reason` is for audit/telemetry
|
|
27
|
+
* only and never surfaced to end users. Boost members still pass the eligibility
|
|
28
|
+
* gate — a boost cannot resurrect a private/restricted/ineligible account.
|
|
29
|
+
*/
|
|
30
|
+
exports.recommendationBoostSchema = zod_1.z.object({
|
|
31
|
+
userIds: zod_1.z.array(zod_1.z.string().trim().min(1)).min(1).max(200),
|
|
32
|
+
weight: zod_1.z.number().min(-5).max(5),
|
|
33
|
+
reason: zod_1.z.string().trim().max(120).optional(),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Per-request overrides for the scoring signal weights. Every key is optional
|
|
37
|
+
* and clamped server-side to the resolved weight profile's allowed range — a
|
|
38
|
+
* caller can re-weight signals but never escape the profile's bounds.
|
|
39
|
+
*/
|
|
40
|
+
exports.recommendationSignalWeightsSchema = zod_1.z
|
|
41
|
+
.object({
|
|
42
|
+
graph: zod_1.z.number().min(0).max(10).optional(),
|
|
43
|
+
completeness: zod_1.z.number().min(0).max(10).optional(),
|
|
44
|
+
verified: zod_1.z.number().min(0).max(10).optional(),
|
|
45
|
+
curation: zod_1.z.number().min(0).max(10).optional(),
|
|
46
|
+
interest: zod_1.z.number().min(0).max(10).optional(),
|
|
47
|
+
appBoost: zod_1.z.number().min(0).max(10).optional(),
|
|
48
|
+
repCandidate: zod_1.z.number().min(0).max(10).optional(),
|
|
49
|
+
affinity: zod_1.z.number().min(0).max(10).optional(),
|
|
50
|
+
})
|
|
51
|
+
.partial();
|
|
52
|
+
/**
|
|
53
|
+
* Request body for `POST /profiles/recommendations`.
|
|
54
|
+
*
|
|
55
|
+
* `clientId` selects the per-app weight profile (the Application `_id`); when
|
|
56
|
+
* omitted the default profile is used. `excludeIds` removes accounts the caller
|
|
57
|
+
* has already seen/handled; `boosts` and `signalWeights` let the caller bias the
|
|
58
|
+
* ranking within server-enforced bounds.
|
|
59
|
+
*/
|
|
60
|
+
exports.recommendationRequestSchema = zod_1.z.object({
|
|
61
|
+
clientId: zod_1.z.string().trim().min(1).optional(),
|
|
62
|
+
limit: zod_1.z.number().int().min(1).max(100).optional(),
|
|
63
|
+
offset: zod_1.z.number().int().min(0).optional(),
|
|
64
|
+
excludeTypes: zod_1.z.array(exports.recommendationExcludeTypeSchema).optional(),
|
|
65
|
+
excludeIds: zod_1.z.array(zod_1.z.string().trim().min(1)).max(500).optional(),
|
|
66
|
+
boosts: zod_1.z.array(exports.recommendationBoostSchema).max(50).optional(),
|
|
67
|
+
signalWeights: exports.recommendationSignalWeightsSchema.optional(),
|
|
68
|
+
});
|
|
69
|
+
/** Follower/following counts attached to a recommendation item. */
|
|
70
|
+
exports.recommendationCountSchema = zod_1.z.object({
|
|
71
|
+
followers: zod_1.z.number().int().nonnegative(),
|
|
72
|
+
following: zod_1.z.number().int().nonnegative(),
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* A single recommended profile.
|
|
76
|
+
*
|
|
77
|
+
* `name` reuses the canonical {@link userNameSchema} so `name.displayName` is the
|
|
78
|
+
* already-resolved server-side value. `score` and `matchedSignals` are present
|
|
79
|
+
* only on the scored (v2) path; `mutualCount` and `_count` are always present.
|
|
80
|
+
*/
|
|
81
|
+
exports.recommendationItemSchema = zod_1.z
|
|
82
|
+
.object({
|
|
83
|
+
id: zod_1.z.string(),
|
|
84
|
+
username: zod_1.z.string().optional(),
|
|
85
|
+
name: userResponse_1.userNameSchema,
|
|
86
|
+
avatar: zod_1.z.string().nullable().optional(),
|
|
87
|
+
description: zod_1.z.string().nullable().optional(),
|
|
88
|
+
verified: zod_1.z.boolean().optional(),
|
|
89
|
+
trustTier: zod_1.z.string().optional(),
|
|
90
|
+
mutualCount: zod_1.z.number().int().nonnegative(),
|
|
91
|
+
score: zod_1.z.number().optional(),
|
|
92
|
+
matchedSignals: zod_1.z.array(zod_1.z.string()).optional(),
|
|
93
|
+
isFederated: zod_1.z.boolean().optional(),
|
|
94
|
+
isAgent: zod_1.z.boolean().optional(),
|
|
95
|
+
isAutomated: zod_1.z.boolean().optional(),
|
|
96
|
+
instance: zod_1.z.string().optional(),
|
|
97
|
+
_count: exports.recommendationCountSchema,
|
|
98
|
+
})
|
|
99
|
+
.passthrough();
|
|
100
|
+
/** Wire shape of the recommendation response — an array of items. */
|
|
101
|
+
exports.recommendationResponseSchema = zod_1.z.array(exports.recommendationItemSchema);
|
|
102
|
+
/** One endorsement edge an app reports: `ownerId` endorses `memberId`. */
|
|
103
|
+
exports.appEndorsementInputSchema = zod_1.z.object({
|
|
104
|
+
ownerId: zod_1.z.string().trim().min(1),
|
|
105
|
+
memberId: zod_1.z.string().trim().min(1),
|
|
106
|
+
op: zod_1.z.enum(['add', 'remove']).default('add'),
|
|
107
|
+
sourceId: zod_1.z.string().trim().min(1).optional(),
|
|
108
|
+
});
|
|
109
|
+
/** One interest signal an app reports: how interested `userId` is in a topic. */
|
|
110
|
+
exports.appInterestInputSchema = zod_1.z.object({
|
|
111
|
+
userId: zod_1.z.string().trim().min(1),
|
|
112
|
+
interestScore: zod_1.z.number().min(0).max(1),
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* Request body for `POST /app-signals/ingest` (service token, `signals:write`).
|
|
116
|
+
*
|
|
117
|
+
* At least one of `endorsements` / `interests` must be non-empty — an ingest
|
|
118
|
+
* with neither is a no-op and rejected so a misconfigured caller is surfaced
|
|
119
|
+
* rather than silently succeeding.
|
|
120
|
+
*/
|
|
121
|
+
exports.appUserSignalIngestSchema = zod_1.z
|
|
122
|
+
.object({
|
|
123
|
+
endorsements: zod_1.z.array(exports.appEndorsementInputSchema).max(500).optional(),
|
|
124
|
+
interests: zod_1.z.array(exports.appInterestInputSchema).max(500).optional(),
|
|
125
|
+
})
|
|
126
|
+
.refine((value) => (value.endorsements?.length ?? 0) > 0 || (value.interests?.length ?? 0) > 0, { message: 'At least one of endorsements or interests must be non-empty' });
|
|
127
|
+
/**
|
|
128
|
+
* The directed interaction types a consuming app may report between two users.
|
|
129
|
+
* Each type carries a server-side default weight (see the API's
|
|
130
|
+
* `AFFINITY_EVENT_WEIGHTS`); a caller may override the applied weight per event.
|
|
131
|
+
*/
|
|
132
|
+
exports.appAffinityEventTypeSchema = zod_1.z.enum([
|
|
133
|
+
'like',
|
|
134
|
+
'reply',
|
|
135
|
+
'boost',
|
|
136
|
+
'follow',
|
|
137
|
+
'mention',
|
|
138
|
+
'profile_view',
|
|
139
|
+
'quote',
|
|
140
|
+
'repost',
|
|
141
|
+
]);
|
|
142
|
+
/**
|
|
143
|
+
* One directed interaction event: `fromUserId` interacted with `toUserId`
|
|
144
|
+
* (`type`) at `occurredAt`. The Oxy affinity-graph folds these into a per-app,
|
|
145
|
+
* time-decayed directed affinity edge (`fromUserId → toUserId`).
|
|
146
|
+
*
|
|
147
|
+
* - `weight` (optional) overrides the per-type default weight for this event.
|
|
148
|
+
* - `occurredAt` (optional, ISO) is the event time; absent means "now" at ingest.
|
|
149
|
+
* - `eventId` (optional) makes an event idempotent — a repeated `eventId` for the
|
|
150
|
+
* same application is folded at most once (bounded dedup window).
|
|
151
|
+
*/
|
|
152
|
+
exports.appAffinityEventSchema = zod_1.z.object({
|
|
153
|
+
fromUserId: zod_1.z.string().trim().min(1),
|
|
154
|
+
toUserId: zod_1.z.string().trim().min(1),
|
|
155
|
+
type: exports.appAffinityEventTypeSchema,
|
|
156
|
+
weight: zod_1.z.number().min(0).max(100).optional(),
|
|
157
|
+
occurredAt: zod_1.z.string().datetime().optional(),
|
|
158
|
+
eventId: zod_1.z.string().trim().min(1).max(200).optional(),
|
|
159
|
+
});
|
|
160
|
+
/**
|
|
161
|
+
* Request body for `POST /app-signals/events` (service token, `signals:write`).
|
|
162
|
+
*
|
|
163
|
+
* A non-empty batch (1..1000) of directed interaction events for the requesting
|
|
164
|
+
* application. Self-edges (`fromUserId === toUserId`) are dropped server-side.
|
|
165
|
+
*/
|
|
166
|
+
exports.appAffinityEventsIngestSchema = zod_1.z.object({
|
|
167
|
+
events: zod_1.z.array(exports.appAffinityEventSchema).min(1).max(1000),
|
|
168
|
+
});
|