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
62 lines
3.1 KiB
TypeScript
62 lines
3.1 KiB
TypeScript
/** Same-origin endpoint descriptors for the future HUX-07 backend. */
|
|
|
|
import type { ArtifactScope, HuxIdentity } from "../artifacts/types.ts";
|
|
import { multimodalEnabled } from "./model.ts";
|
|
import { normalizeIdentity, normalizeScope, safeEndpointPart, sameIdentity, sameScope } from "./security.ts";
|
|
import type {
|
|
CaptureKind, MultimodalEndpoint, MultimodalEndpointContract,
|
|
} from "./types.ts";
|
|
|
|
interface FoundationClient {
|
|
apiVersion: string;
|
|
identity: HuxIdentity;
|
|
endpoint(path: string): string;
|
|
}
|
|
|
|
function endpoint(method: MultimodalEndpoint["method"], path: string,
|
|
responseSchema: string, requiredBody?: readonly string[]): MultimodalEndpoint {
|
|
return Object.freeze({method, path, responseSchema, ...(requiredBody ? {requiredBody} : {})});
|
|
}
|
|
|
|
/** Describe endpoints only. The caller owns authenticated transport and approval execution. */
|
|
export function createMultimodalEndpointContract(client: FoundationClient, scope: ArtifactScope,
|
|
flags?: Iterable<string>): MultimodalEndpointContract | null {
|
|
if (!multimodalEnabled(flags)) return null;
|
|
if (client.apiVersion !== "hux.v1" || !sameIdentity(normalizeIdentity(client.identity), client.identity) ||
|
|
!sameScope(normalizeScope(scope), scope)) throw new TypeError("Scoped HUX v1 client required");
|
|
const base = client.endpoint(`/projects/${safeEndpointPart(scope.projectId, "prj")}` +
|
|
`/conversations/${safeEndpointPart(scope.conversationId, "conv")}/multimodal`);
|
|
const child = (root: string, id: unknown, prefix: string, suffix = "") =>
|
|
`${base}/${root}/${safeEndpointPart(id, prefix)}${suffix}`;
|
|
return Object.freeze({
|
|
page: endpoint("GET", base, "hux.multimodal_page.v1"),
|
|
beginUpload: endpoint("POST", `${base}/uploads`, "hux.media_upload.v1",
|
|
["file_name", "mime", "bytes", "authorization_id"]),
|
|
finalizeUpload(uploadId) {
|
|
return endpoint("POST", child("uploads", uploadId, "upl", "/finalize"), "hux.media.v1",
|
|
["content_ref", "expected_upload_id"]);
|
|
},
|
|
editImage(mediaId) {
|
|
return endpoint("POST", child("media", mediaId, "med", "/edits"), "hux.media.v1",
|
|
["artifact_id", "expected_current_version", "instruction", "lineage"]);
|
|
},
|
|
annotate(mediaId) {
|
|
return endpoint("POST", child("media", mediaId, "med", "/annotations"), "hux.media.v1",
|
|
["artifact_id", "expected_current_version", "page", "note"]);
|
|
},
|
|
correctTranscript(transcriptId) {
|
|
return endpoint("POST", child("transcripts", transcriptId, "trn", "/corrections"),
|
|
"hux.voice_transcript.v1", ["turn_id", "expected_revision", "corrected_text"]);
|
|
},
|
|
requestCapture(kind: CaptureKind) {
|
|
if (!['camera', 'screen'].includes(kind)) throw new TypeError("Capture kind is invalid");
|
|
return endpoint("POST", `${base}/captures/${kind}/approvals`, "hux.approval.v1",
|
|
["capability", "required_decision", "conversation_id"]);
|
|
},
|
|
createVariant(threadId) {
|
|
return endpoint("POST", child("threads", threadId, "thr", "/variants"),
|
|
"hux.generation_thread.v1", ["media_id", "artifact_id", "artifact_version", "parent_variant_id"]);
|
|
},
|
|
});
|
|
}
|