Standalone per-card browser model/security/view modules for HUX-01..10 plus node+pytest suites that read the hux.v1 contract schemas directly. Reconciled drift found on integration: the activity model now accepts all 32 hux.event.v1 kinds (delegation.*, memory.suppressed, memory.retrieval_removed, budget.exhausted, side_effect.*), the autonomy model carries the external_side_effect capability, and the foundation boundary test now asserts the shipped static HUX surface exists on disk and that images never bake activated HUX_FLAGS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
47 lines
2.4 KiB
TypeScript
47 lines
2.4 KiB
TypeScript
/** Same-origin endpoint descriptors for the future HUX-08 backend. */
|
|
|
|
import { researchEnabled } from "./model.ts";
|
|
import { normalizeIdentity, normalizeScope, safeMessageId, safePathId, sameIdentity, sameScope } from "./security.ts";
|
|
import type { HuxIdentity, ResearchEndpoint, ResearchEndpointContract, ResearchScope } from "./types.ts";
|
|
|
|
interface FoundationClient {
|
|
apiVersion: string;
|
|
identity: HuxIdentity;
|
|
endpoint(path: string): string;
|
|
}
|
|
|
|
function endpoint(method: ResearchEndpoint["method"], path: string, responseSchema: string,
|
|
requiredBody?: readonly string[]): ResearchEndpoint {
|
|
return Object.freeze({method, path, responseSchema, ...(requiredBody ? {requiredBody} : {})});
|
|
}
|
|
|
|
/** Describes authenticated endpoints without issuing network requests. */
|
|
export function createResearchEndpointContract(client: FoundationClient, scope: ResearchScope,
|
|
flags?: Iterable<string>): ResearchEndpointContract | null {
|
|
if (!researchEnabled(flags)) return null;
|
|
const rawIdentity = {tenant_ref: client.identity.tenantRef, user_ref: client.identity.userRef,
|
|
surface: client.identity.surface};
|
|
const rawScope = {project_id: scope.projectId, conversation_id: scope.conversationId};
|
|
if (client.apiVersion !== "hux.v1" || !sameIdentity(normalizeIdentity(rawIdentity), client.identity) ||
|
|
!sameScope(normalizeScope(rawScope), scope)) throw new TypeError("Scoped HUX v1 research client required");
|
|
const base = client.endpoint(`/projects/${safePathId(scope.projectId, "prj")}` +
|
|
`/conversations/${safePathId(scope.conversationId, "conv")}/research`);
|
|
return Object.freeze({
|
|
page: endpoint("GET", base, "hux.research_page.v1"),
|
|
citationsForMessage(messageId) {
|
|
return endpoint("GET", `${base}/messages/${safeMessageId(messageId)}/citations`, "hux.citation_page.v1");
|
|
},
|
|
notebook(notebookId) {
|
|
return endpoint("GET", `${base}/notebooks/${safePathId(notebookId, "nb")}`, "hux.research_notebook.v1");
|
|
},
|
|
updateNotebook(notebookId) {
|
|
return endpoint("PUT", `${base}/notebooks/${safePathId(notebookId, "nb")}`, "hux.research_notebook.v1",
|
|
["expected_updated_at", "status", "assumptions", "unresolved_questions"]);
|
|
},
|
|
attachCitation(messageId) {
|
|
return endpoint("POST", `${base}/messages/${safeMessageId(messageId)}/citations`, "hux.citation.v1",
|
|
["claim", "passage_ids", "support", "note"]);
|
|
},
|
|
});
|
|
}
|